]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'ml/log-merge-with-cherry-pick-and-other-pseudo-heads'
authorJunio C Hamano <gitster@pobox.com>
Thu, 7 Mar 2024 23:59:41 +0000 (15:59 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 7 Mar 2024 23:59:41 +0000 (15:59 -0800)
"git log --merge" learned to pay attention to CHERRY_PICK_HEAD and
other kinds of *_HEAD pseudorefs.

* ml/log-merge-with-cherry-pick-and-other-pseudo-heads:
  revision: implement `git log --merge` also for rebase/cherry-pick/revert
  revision: ensure MERGE_HEAD is a ref in prepare_show_merge

358 files changed:
.cirrus.yml
.github/PULL_REQUEST_TEMPLATE.md
.github/workflows/check-whitespace.yml
.github/workflows/coverity.yml
.github/workflows/l10n.yml
.github/workflows/main.yml
.gitlab-ci.yml
CODE_OF_CONDUCT.md
Documentation/CodingGuidelines
Documentation/MyFirstContribution.txt
Documentation/RelNotes/2.43.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.43.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.43.3.txt [new file with mode: 0644]
Documentation/RelNotes/2.44.0.txt
Documentation/RelNotes/2.45.0.txt [new file with mode: 0644]
Documentation/config/advice.txt
Documentation/config/diff.txt
Documentation/config/feature.txt
Documentation/config/fetch.txt
Documentation/config/interactive.txt
Documentation/config/mergetool.txt
Documentation/config/pack.txt
Documentation/config/sendemail.txt
Documentation/diff-options.txt
Documentation/fetch-options.txt
Documentation/git-add.txt
Documentation/git-am.txt
Documentation/git-bisect.txt
Documentation/git-blame.txt
Documentation/git-branch.txt
Documentation/git-bugreport.txt
Documentation/git-clone.txt
Documentation/git-commit-graph.txt
Documentation/git-config.txt
Documentation/git-cvsserver.txt
Documentation/git-daemon.txt
Documentation/git-diagnose.txt
Documentation/git-difftool.txt
Documentation/git-fast-export.txt
Documentation/git-fast-import.txt
Documentation/git-fetch.txt
Documentation/git-filter-branch.txt
Documentation/git-for-each-ref.txt
Documentation/git-format-patch.txt
Documentation/git-index-pack.txt
Documentation/git-ls-files.txt
Documentation/git-merge-tree.txt
Documentation/git-mv.txt
Documentation/git-notes.txt
Documentation/git-rebase.txt
Documentation/git-reflog.txt
Documentation/git-remote.txt
Documentation/git-replace.txt
Documentation/git-rev-parse.txt
Documentation/git-revert.txt
Documentation/git-send-email.txt
Documentation/git-status.txt
Documentation/git-submodule.txt
Documentation/git-svn.txt
Documentation/git-tag.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitdiffcore.txt
Documentation/gitformat-index.txt
Documentation/githooks.txt
Documentation/gitk.txt
Documentation/gitprotocol-capabilities.txt
Documentation/gitprotocol-http.txt
Documentation/gitprotocol-v2.txt
Documentation/gitsubmodules.txt
Documentation/gitweb.conf.txt
Documentation/gitweb.txt
Documentation/glossary-content.txt
Documentation/mergetools/vimdiff.txt
Documentation/ref-storage-format.txt
Documentation/rev-list-options.txt
Documentation/technical/repository-version.txt
Documentation/trace2-target-values.txt
Documentation/urls.txt
Documentation/user-manual.txt
GIT-VERSION-GEN
Makefile
README.md
RelNotes
add-patch.c
advice.c
advice.h
apply.c
archive-tar.c
bisect.c
branch.c
builtin/add.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/column.c
builtin/commit-graph.c
builtin/commit.c
builtin/config.c
builtin/fast-export.c
builtin/fetch.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/gc.c
builtin/grep.c
builtin/index-pack.c
builtin/merge-tree.c
builtin/merge.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/read-tree.c
builtin/rebase.c
builtin/receive-pack.c
builtin/reflog.c
builtin/reset.c
builtin/rev-list.c
builtin/send-pack.c
builtin/show-ref.c
builtin/stash.c
builtin/tag.c
builtin/var.c
builtin/worktree.c
cache-tree.c
ci/install-dependencies.sh
ci/install-docker-dependencies.sh
ci/lib.sh
ci/print-test-failures.sh
ci/run-build-and-minimal-fuzzers.sh [new file with mode: 0755]
ci/run-build-and-tests.sh
column.c
combine-diff.c
commit-graph.c
compat/compiler.h
compat/disk.h
compat/mingw.c
config.c
config.h
config.mak.uname
contrib/coccinelle/xstrncmpz.cocci [new file with mode: 0644]
contrib/completion/git-completion.bash
contrib/completion/git-prompt.sh
contrib/credential/libsecret/git-credential-libsecret.c
contrib/credential/wincred/git-credential-wincred.c
contrib/subtree/git-subtree.sh
contrib/subtree/t/t7900-subtree.sh
contrib/workdir/git-new-workdir
convert.c
diff.c
diffcore-delta.c
dir-iterator.c
dir-iterator.h
environment.c
environment.h
fetch-pack.c
fsck.c
git-difftool--helper.sh
git-p4.py
git.c
gitweb/gitweb.perl
gpg-interface.c
gpg-interface.h
http-backend.c
http-push.c
imap-send.c
list-objects-filter.c
mem-pool.c
mem-pool.h
merge-ll.c
merge-ort.c
merge-recursive.c
merge.c
mergetools/vimdiff
neue [deleted file]
object-name.c
object.c
oidset.c
oidset.h
oss-fuzz/dummy-cmd-main.c [new file with mode: 0644]
pack-bitmap.c
parse-options.c
path.c
path.h
po/bg.po
po/ca.po
po/de.po
po/fr.po
po/id.po
po/sv.po
po/tr.po
po/uk.po
po/zh_CN.po
po/zh_TW.po
reachable.c
ref-filter.c
ref-filter.h
refs.c
refs.h
refs/debug.c
refs/files-backend.c
refs/iterator.c
refs/packed-backend.c
refs/ref-cache.c
refs/refs-internal.h
refs/reftable-backend.c [new file with mode: 0644]
reftable/basics.c
reftable/basics.h
reftable/block.c
reftable/block_test.c
reftable/blocksource.c
reftable/iter.c
reftable/merged.c
reftable/merged.h
reftable/merged_test.c
reftable/pq.c
reftable/pq_test.c
reftable/publicbasics.c
reftable/reader.c
reftable/readwrite_test.c
reftable/record.c
reftable/record.h
reftable/record_test.c
reftable/refname.c
reftable/refname_test.c
reftable/reftable-merged.h
reftable/reftable-writer.h
reftable/stack.c
reftable/stack.h
reftable/stack_test.c
reftable/test_framework.c
reftable/test_framework.h
reftable/tree.c
reftable/writer.c
reftable/writer.h
remote-curl.c
remote.c
repo-settings.c
repository.c
repository.h
rerere.c
reset.c
revision.c
sequencer.c
sequencer.h
setup.c
shared.mak
sparse-index.c
strvec.h
submodule-config.c
submodule-config.h
submodule.c
t/Makefile
t/helper/test-ctype.c [deleted file]
t/helper/test-prio-queue.c [deleted file]
t/helper/test-ref-store.c
t/helper/test-submodule.c
t/helper/test-tool.c
t/helper/test-tool.h
t/lib-credential.sh
t/t0009-prio-queue.sh [deleted file]
t/t0018-advice.sh
t/t0024-crlf-archive.sh
t/t0035-safe-bare-repository.sh
t/t0070-fundamental.sh
t/t0080-unit-test-output.sh
t/t0091-bugreport.sh
t/t0303-credential-external.sh
t/t0410-partial-clone.sh
t/t0600-reffiles-backend.sh [new file with mode: 0755]
t/t0601-reffiles-pack-refs.sh [moved from t/t3210-pack-refs.sh with 81% similarity]
t/t0610-reftable-basics.sh [new file with mode: 0755]
t/t0611-reftable-httpd.sh [new file with mode: 0755]
t/t1300-config.sh
t/t1301-shared-repo.sh
t/t1302-repo-version.sh
t/t1400-update-ref.sh
t/t1401-symbolic-ref.sh
t/t1403-show-ref.sh
t/t1404-update-ref-errors.sh
t/t1405-main-ref-store.sh
t/t1406-submodule-ref-store.sh
t/t1407-worktree-ref-store.sh
t/t1409-avoid-packing-refs.sh
t/t1410-reflog.sh
t/t1414-reflog-walk.sh
t/t1415-worktree-refs.sh
t/t1419-exclude-refs.sh
t/t1503-rev-parse-verify.sh
t/t2011-checkout-invalid-head.sh
t/t2016-checkout-patch.sh
t/t2017-checkout-orphan.sh
t/t2020-checkout-detach.sh
t/t2024-checkout-dwim.sh
t/t2071-restore-patch.sh
t/t2400-worktree-add.sh
t/t3200-branch.sh
t/t3202-show-branch.sh
t/t3400-rebase.sh
t/t3404-rebase-interactive.sh
t/t3407-rebase-abort.sh
t/t3420-rebase-autostash.sh
t/t3903-stash.sh
t/t3904-stash-patch.sh
t/t4001-diff-rename.sh
t/t4013-diff-various.sh
t/t4042-diff-textconv-caching.sh
t/t4053-diff-no-index.sh
t/t4129-apply-samemode.sh
t/t4202-log.sh
t/t4301-merge-tree-write-tree.sh
t/t5003-archive-zip.sh
t/t5300-pack-object.sh
t/t5312-prune-corruption.sh
t/t5332-multi-pack-reuse.sh
t/t5514-fetch-multiple.sh
t/t5526-fetch-submodules.sh
t/t5541-http-push-smart.sh
t/t5551-http-fetch-smart.sh
t/t6022-rev-list-missing.sh
t/t6030-bisect-porcelain.sh
t/t6113-rev-list-bitmap-filters.sh
t/t6300-for-each-ref.sh
t/t6302-for-each-ref-filter.sh
t/t6406-merge-attr.sh
t/t6437-submodule-merge.sh
t/t7003-filter-branch.sh
t/t7105-reset-patch.sh
t/t7106-reset-unborn-branch.sh
t/t7402-submodule-rebase.sh
t/t7450-bad-git-dotfiles.sh
t/t7501-commit-basic-functionality.sh
t/t7502-commit-porcelain.sh
t/t7513-interpret-trailers.sh
t/t7514-commit-patch.sh
t/t7527-builtin-fsmonitor.sh
t/t7800-difftool.sh
t/t7900-maintenance.sh
t/t9002-column.sh
t/t9146-git-svn-empty-dirs.sh
t/t9210-scalar.sh
t/t9902-completion.sh
t/test-lib-github-workflow-markup.sh
t/test-lib.sh
t/unit-tests/t-ctype.c [new file with mode: 0644]
t/unit-tests/t-prio-queue.c [new file with mode: 0644]
t/unit-tests/test-lib.c
trailer.c
transport-helper.c
transport.c
tree-walk.c
upload-pack.c
userdiff.c
worktree.c
worktree.h
write-or-die.c
wt-status.c
wt-status.h

index b6280692d2f21aaa17548985979279594c94323e..77346a4929d4eb6af398cbf0761016d8c6d34347 100644 (file)
@@ -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 @@ freebsd_12_task:
     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
index 952c7c3a2aa11ea1087390be61eab6f7c0013599..37654cdfd7abcf7ca310ecf2c67c694fb94ad113 100644 (file)
@@ -4,4 +4,7 @@ a mailing list (git@vger.kernel.org) for code submissions, code reviews, and
 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!
index a58e2dc8ad6279d792635c4034ed0d113d489695..a241a63428bd20e0c28a61ab6de56cbb39966c42 100644 (file)
@@ -19,7 +19,7 @@ jobs:
   check-whitespace:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
       with:
         fetch-depth: 0
 
index e5532d381bcb15cb74a5e4b91927815d489aa0c3..53cf12fe04442561e94f7176eb69c1e8e14a62f3 100644 (file)
@@ -38,7 +38,7 @@ jobs:
       COVERITY_LANGUAGE: cxx
       COVERITY_PLATFORM: overridden-below
     steps:
-      - uses: actions/checkout@v3
+      - 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
@@ -98,7 +98,7 @@ jobs:
       # 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@v3
+        uses: actions/cache/restore@v4
         with:
           path: ${{ runner.temp }}/cov-analysis
           key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }}
@@ -141,7 +141,7 @@ jobs:
           esac
       - name: cache the Coverity Build Tool
         if: steps.cache.outputs.cache-hit != 'true'
-        uses: actions/cache/save@v3
+        uses: actions/cache/save@v4
         with:
           path: ${{ runner.temp }}/cov-analysis
           key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }}
index 6c3849658aa061b89ced8e0464b3b580519c3a34..e2c3dbdcb50f0cedbf990677e234e7114ecd7746 100644 (file)
@@ -63,9 +63,10 @@ jobs:
             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 @@ jobs:
           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
index 9fdbd5402898bf1c8a188bfd24262109c96e5e0f..683a2d633ed5a95f7daab4ffb8ba5cf1ed8d0634 100644 (file)
@@ -63,7 +63,7 @@ jobs:
           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}}
@@ -112,7 +112,7 @@ jobs:
       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
@@ -123,7 +123,7 @@ jobs:
     - 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
@@ -140,7 +140,7 @@ jobs:
       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}}
@@ -157,7 +157,7 @@ jobs:
       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
         path: ${{env.FAILED_TEST_ARTIFACTS}}
@@ -173,10 +173,10 @@ jobs:
       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'
@@ -212,7 +212,7 @@ jobs:
     - 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
@@ -230,7 +230,7 @@ jobs:
     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}}
@@ -248,7 +248,7 @@ jobs:
       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
         path: ${{env.FAILED_TEST_ARTIFACTS}}
@@ -266,6 +266,9 @@ jobs:
           - jobname: linux-sha256
             cc: clang
             pool: ubuntu-latest
+          - jobname: linux-reftable
+            cc: clang
+            pool: ubuntu-latest
           - jobname: linux-gcc
             cc: gcc
             cc_package: gcc-8
@@ -277,6 +280,9 @@ jobs:
           - jobname: osx-clang
             cc: clang
             pool: macos-13
+          - jobname: osx-reftable
+            cc: clang
+            pool: macos-13
           - jobname: osx-gcc
             cc: gcc
             cc_package: gcc-13
@@ -287,6 +293,9 @@ jobs:
           - jobname: linux-leaks
             cc: gcc
             pool: ubuntu-latest
+          - jobname: linux-reftable-leaks
+            cc: gcc
+            pool: ubuntu-latest
           - jobname: linux-asan-ubsan
             cc: clang
             pool: ubuntu-latest
@@ -297,7 +306,7 @@ jobs:
       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
@@ -305,10 +314,21 @@ jobs:
       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
@@ -331,9 +351,9 @@ jobs:
     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
@@ -342,13 +362,13 @@ jobs:
       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}}
@@ -362,7 +382,7 @@ jobs:
       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
@@ -385,7 +405,7 @@ jobs:
         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
@@ -400,6 +420,6 @@ jobs:
       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
index cd98bcb18aa38aca55f577466f7d44f06c50177f..c0fa2fe90b4b81aad0d1ce6f50770cbbbebcebf9 100644 (file)
@@ -7,7 +7,7 @@ workflow:
     - if: $CI_COMMIT_TAG
     - if: $CI_COMMIT_REF_PROTECTED == "true"
 
-test:
+test:linux:
   image: $image
   before_script:
     - ./ci/install-docker-dependencies.sh
@@ -26,6 +26,9 @@ test:
       - 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
@@ -40,6 +43,9 @@ test:
       - 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
@@ -51,3 +57,48 @@ test:
     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
index 0215b1fd4c05e668f37973549384aae24dcc65cf..e58917c50a96dc9457a983bbf2b46ed00f2f3e90 100644 (file)
@@ -130,11 +130,11 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
 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
index 578587a47155e929457e12862cd648c9fdf81acd..32e69f798ee7da4174c1f73910c899adf14ca63c 100644 (file)
@@ -446,12 +446,41 @@ For C programs:
    detail.
 
  - The first #include in C files, except in platform specific compat/
-   implementations and sha1dc/, must be either "git-compat-util.h" or
-   one of the approved headers that includes it first for you.  (The
-   approved headers currently include "builtin.h",
-   "t/helper/test-tool.h", "xdiff/xinclude.h", or
-   "reftable/system.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
@@ -666,6 +695,11 @@ Writing Documentation:
    <new-branch-name>
    --template=<template-directory>
 
+ When a placeholder is cited in text paragraph, it is enclosed in angle
+ brackets to remind the reader the reference in the synopsis section.
+ For better visibility, the placeholder is typeset in italics:
+   The _<file>_ to be added.
+
  Possibility of multiple occurrences is indicated by three dots:
    <file>...
    (One or more of <file>.)
@@ -751,6 +785,8 @@ Writing Documentation:
    Incorrect:
       `\--pretty=oneline`
 
+A placeholder is not enclosed in backticks, as it is not a literal.
+
  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
index 279f6a3e7ca4678f01196e9f60cdea372d0cc4f1..f06563e98174604d339e3ded16b762e13b41534a 100644 (file)
@@ -35,8 +35,9 @@ announcements, design discussions, and more take place. Those interested in
 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]
diff --git a/Documentation/RelNotes/2.43.1.txt b/Documentation/RelNotes/2.43.1.txt
new file mode 100644 (file)
index 0000000..20e96f2
--- /dev/null
@@ -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 (file)
index 0000000..5895e23
--- /dev/null
@@ -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 (file)
index 0000000..924f205
--- /dev/null
@@ -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.
index 2b9d2bd3c260be8e114fc751cf5215a2e7269a88..14f9ce822623cf08f10124bc4db378e537dc6841 100644 (file)
@@ -3,7 +3,7 @@ Git v2.44 Release Notes
 
 Backward Compatibility Notes
 
- * "git chekcout -B <branch>" used to allow switching to a branch that
+ * "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.
 
@@ -38,6 +38,70 @@ UI, Workflows & Features
    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.
 
@@ -72,13 +136,50 @@ Performance, Internal Implementation, Development Support etc.
    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.
-   (merge e7e03ef995 js/ci-discard-prove-state later to maint).
 
  * Update ref-related tests.
 
@@ -89,75 +190,59 @@ Fixes since v2.43
    (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.
-   (merge 6ff658cc78 tz/send-email-negatable-options later to maint).
 
  * 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.
-   (merge b1df3b3867 ps/commit-graph-less-paranoid later to maint).
 
  * Stale URLs have been updated to their current counterparts (or
    archive.org) and HTTP links are replaced with working HTTPS links.
-   (merge 62b4f7b9c6 js/update-urls-in-doc-and-comment later to maint).
 
  * trace2 streams used to record the URLs that potentially embed
    authentication material, which has been corrected.
-   (merge 16fa3eebc0 jh/trace2-redact-auth later to maint).
 
  * 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.
-   (merge d9fd71fa2a jp/use-diff-index-in-pre-commit-sample later to maint).
 
  * 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.
-   (merge 71a1e94821 jc/revision-parse-int later to maint).
 
  * "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".
-   (merge 9385174627 jk/end-of-options later to maint).
 
  * "git bisect reset" has been taught to clean up state files and refs
    even when BISECT_START file is gone.
-   (merge daaa03e54c jk/bisect-reset-fix later to maint).
 
  * Some codepaths did not correctly parse configuration variables
    specified with valueless "true", which has been corrected.
-   (merge d49cb162fa jk/implicit-true later to maint).
 
  * Code clean-up for sanity checking of command line options for "git
    show-ref".
-   (merge 7382497372 rs/show-ref-incompatible-options later to maint).
 
  * The code to parse the From e-mail header has been updated to avoid
    recursion.
-   (merge dee182941f jk/mailinfo-iterative-unquote-comment later to maint).
 
  * "git fetch --atomic" issued an unnecessary empty error message,
    which has been corrected.
-   (merge 18ce48918c jx/fetch-atomic-error-message-fix later to maint).
 
  * Command line completion script (in contrib/) learned to work better
    with the reftable backend.
-   (merge 44dbb3bf29 sh/completion-with-reftable later to maint).
 
  * "git status" is taught to show both the branch being bisected and
    being rebased when both are in effect at the same time.
-   (merge 990adccbdf rj/status-bisect-while-rebase later to maint).
 
  * "git archive --list extra garbage" silently ignored excess command
    line parameters, which has been corrected.
-   (merge d6b6cd1393 jc/archive-list-with-extra-args later to maint).
 
  * "git sparse-checkout set" added default patterns even when the
    patterns are being fed from the standard input, which has been
    corrected.
-   (merge 53ded839ae jc/sparse-checkout-set-default-fix later to maint).
 
  * "git sparse-checkout (add|set) --[no-]cone --end-of-options" did
    not handle "--end-of-options" correctly after a recent update.
@@ -165,42 +250,85 @@ Fixes since v2.43
  * 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.
-   (merge 556e68032f cp/git-flush-is-an-env-bool later to maint).
 
  * 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.
-   (merge d70f554cdf jk/commit-graph-slab-clear-fix later to maint).
+
+ * 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 50f1abcff6 js/packfile-h-typofix later to maint).
-   (merge cbf498eb53 jb/reflog-expire-delete-dry-run-options later to maint).
-   (merge 7854bf4960 rs/i18n-cannot-be-used-together later to maint).
-   (merge cd3c28c53a rs/column-leakfix later to maint).
-   (merge 866a1b9026 ps/ref-tests-update-more later to maint).
-   (merge e4299d26d4 mk/doc-gitfile-more later to maint).
-   (merge 792b86283b rs/incompatible-options-messages later to maint).
-   (merge ea8f9494ab jk/config-cleanup later to maint).
-   (merge d1bd3a8c34 jk/mailinfo-oob-read-fix later to maint).
-   (merge c0cadb0576 ps/reftable-fixes later to maint).
-   (merge 647b5e0998 ps/chainlint-self-check-update later to maint).
-   (merge 68fcebfb1a es/add-doc-list-short-form-of-all-in-synopsis later to maint).
-   (merge bc62d27d5c jc/doc-most-refs-are-not-that-special later to maint).
-   (merge 6d6f1cd7ee jc/doc-misspelt-refs-fix later to maint).
-   (merge 37e8d795be sp/test-i18ngrep later to maint).
-   (merge fbc6526ea6 rs/t6300-compressed-size-fix later to maint).
-   (merge 45184afb4d rs/rebase-use-strvec-pushf later to maint).
-   (merge a762af3dfd jc/retire-cas-opt-name-constant later to maint).
-   (merge de7c27a186 la/trailer-cleanups later to maint).
-   (merge d44b517137 jc/orphan-unborn later to maint).
-   (merge 63956c553d ml/doc-merge-updates later to maint).
-   (merge d57c671a51 en/header-cleanup later to maint).
-   (merge 5b7eec4bc5 rs/fast-import-simplify-mempool-allocation later to maint).
-   (merge 291873e5d6 js/contributor-docs-updates later to maint).
-   (merge 54d8a2531b jk/t1006-cat-file-objectsize-disk later to maint).
-   (merge 7033d5479b jx/sideband-chomp-newline-fix later to maint).
-   (merge 9cd30af991 ms/rebase-insnformat-doc-fix later to maint).
-   (merge 03bcc93769 cp/sideband-array-index-comment-fix later to maint).
-   (merge 993d38a066 jk/index-pack-lsan-false-positive-fix later to maint).
-   (merge 25aec06326 ib/rebase-reschedule-doc later to maint).
+   (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 (file)
index 0000000..fe8832b
--- /dev/null
@@ -0,0 +1,110 @@
+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.
+
+
+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.
+
+
+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).
+
+ * 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).
index 4d7e5d875998fe1c3aa20c0bb5efdcf9fb6fad91..dde8e7840ef38225995549411fbcf9305b160c37 100644 (file)
@@ -1,30 +1,63 @@
 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::
+               Advice on what to do when you've accidentally added one
+               git repo inside of another.
+       addEmptyPathspec::
+               Advice shown if a user runs the add command without providing
+               the pathspec parameter.
+       addIgnoredFile::
+               Advice shown if a user attempts to add an ignored file to
+               the index.
+       amWorkDir::
+               Advice that shows the location of the patch file when
+               linkgit:git-am[1] fails to apply it.
        ambiguousFetchRefspec::
                Advice 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::
+               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 be used by default in some situations where this
+               advice would be printed.
+       commitBeforeMerge::
+               Advice shown when linkgit:git-merge[1] refuses to
+               merge to avoid overwriting local changes.
+       detachedHead::
+               Advice shown when you used
+               linkgit:git-switch[1] or linkgit:git-checkout[1]
+               to move to the detached HEAD state, to instruct how to
+               create a local branch after the fact.
+       diverging::
+               Advice shown when a fast-forward is not possible.
        fetchShowForcedUpdates::
                Advice 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::
+               Advice shown when a user tries to delete a not fully merged
+               branch without the force option set.
+       ignoredHook::
+               Advice shown if a hook is ignored because the hook is not
+               set as executable.
+       implicitIdentity::
+               Advice on how to set your identity configuration when
+               your information is guessed from the system username and
+               domain name.
+       nestedTag::
+               Advice shown if 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,6 +70,18 @@ advice.*::
                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::
+               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.
+       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
@@ -44,10 +89,23 @@ advice.*::
                we can still suggest that the user push to either
                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.
+       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.
+       rmHints::
+               In case of failure in the output of linkgit:git-rm[1],
+               show directions on how to proceed from the current state.
+       sequencerInUse::
+               Advice 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.
@@ -68,76 +126,25 @@ advice.*::
                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 detached 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 be 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.
        submoduleAlternateErrorStrategyDie::
                Advice 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
                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::
+               Advice 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]
                is asked to update index entries outside the current sparse
                checkout.
-       diverging::
-               Advice shown when a fast-forward is not possible.
+       waitingForEditor::
+               Print a message to the terminal whenever Git is waiting for
+               editor input from the user.
        worktreeAddOrphan::
                Advice shown when a user tries to create a worktree from an
                invalid reference, to instruct how to create a new unborn
index bd5ae0c3378adbd5661a202dc1f8fcfbf27ac4a1..6c7e09a1ef5eb481b2abb26abd93edf38806cf76 100644 (file)
@@ -223,5 +223,5 @@ diff.colorMoved::
 
 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].
index bf9546fca4f693f01b39252d12def7df9b51a52f..f061b64b7484497a43fba7aaac67c7f84b012582 100644 (file)
@@ -17,6 +17,9 @@ skipping more commits at a time, reducing the number of round trips.
 +
 * `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
index aea5b97477b64ef664b6b35faad78b2eb618db9a..d7dc461bd16ad782933a40e060c523b063cb1aca 100644 (file)
@@ -50,6 +50,12 @@ fetch.pruneTags::
        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 the
index a2d3c7ec449e9b943a71f21891c2fd733a695dfc..5cc26555f19a4ff8f8a290a5316bfdd07b506e77 100644 (file)
@@ -4,9 +4,7 @@ interactive.singleKey::
        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
index 294f61efd12fdf863e648b076462f4a9a1aa75b7..00bf665aa09bf353dd2c81220d2c83a31086e5b7 100644 (file)
@@ -45,14 +45,21 @@ mergetool.meld.useAutoMerge::
        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 appear. Applies even if you are using Neovim (`nvim`) or
-       gVim (`gvim`) as the merge tool. See BACKEND SPECIFIC HINTS section
+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::[]
 ifndef::git-mergetool[]
-       in linkgit:git-mergetool[1].
+       section in linkgit:git-mergetool[1].
 endif::[]
-       for details.
 
 mergetool.hideResolved::
        During a merge, Git will automatically resolve as many conflicts as
index 9c630863e6ff18372172111619d4ad2a54f3264f..da527377fafcb629108676ed0becc489ed25095a 100644 (file)
@@ -34,11 +34,10 @@ pack.allowPackReuse::
        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.
+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
index 7fc770ee9e69d77b3743c8bff1b8bef35bb96f36..6a869d67eb90c9992a1cbcf3efb372d3a6fb2f40 100644 (file)
@@ -8,7 +8,7 @@ sendemail.smtpEncryption::
        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.
 
@@ -62,12 +62,12 @@ sendemail.chainReplyTo::
 sendemail.envelopeSender::
 sendemail.from::
 sendemail.headerCmd::
-sendemail.signedoffbycc::
+sendemail.signedOffByCc::
 sendemail.smtpPass::
-sendemail.suppresscc::
+sendemail.suppressCc::
 sendemail.suppressFrom::
 sendemail.to::
-sendemail.tocmd::
+sendemail.toCmd::
 sendemail.smtpDomain::
 sendemail.smtpServer::
 sendemail.smtpServerPort::
@@ -81,8 +81,8 @@ sendemail.xmailer::
        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
index 53ec3c9a3476bd12b8a8f4b6c31552b2767d8366..aaaff0d46f0c6f04ca4e3182872b47d2bd2f07b6 100644 (file)
@@ -299,7 +299,7 @@ and accumulating child directory counts in the parent directories:
        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
index a1d6633a4f15b719a33615ca0ce2805ec2fa0b3e..54ebb4452e997f8c62c9dd2421948086249102bd 100644 (file)
@@ -1,5 +1,6 @@
---all::
-       Fetch all remotes.
+--[no-]all::
+       Fetch all remotes. This overrides the configuration variable
+       `fetch.all`.
 
 -a::
 --append::
index 3d2e6707168b69d591e3bf2a50abe3a62fc2ee23..14a371fff3569eac4fd633ebc945b1d8793b1237 100644 (file)
@@ -63,7 +63,7 @@ OPTIONS
        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 @@ apply to the index. See EDITING PATCHES below.
 -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 @@ subdirectories).
 --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 @@ subdirectories).
        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 @@ for "git add --no-all <pathspec>...", i.e. ignored removed files.
        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
index e080458d6c45891ec1db74b9df155aeab111e79f..463a3c660024e08b494caa3e22b4d4c417f46e0c 100644 (file)
@@ -128,6 +128,9 @@ include::rerere-options.txt[]
        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
index aa02e462244ee0ad2836412cb2351fc43e9c43ad..82f944dc03dffccae2425cb3a250d681ad3ed134 100644 (file)
@@ -16,11 +16,11 @@ DESCRIPTION
 The command takes various subcommands, and different options depending
 on the subcommand:
 
- git bisect start [--term-(new|bad)=<term-new> --term-(old|good)=<term-old>]
-                 [--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)
@@ -165,8 +165,10 @@ To get a reminder of the currently used terms, use
 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
@@ -299,7 +301,7 @@ Cutting down bisection by giving more parameters to bisect start
 
 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
index 5720d04ffe4e7434c1798cad4833e233fc71f534..b1d7fb539d021664111276e33ed16dfcd2a91f3c 100644 (file)
@@ -210,7 +210,7 @@ annotated.
 
 . 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.
 
index 4395aa935438aaac47cfe41f5db7a1eadafe8a25..0b08442932354fcdcbb321f0c45b80a3a5bc4fdf 100644 (file)
@@ -312,7 +312,8 @@ superproject's "origin/main", but tracks the submodule's "origin/main".
        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
index 392d9eb6aec6b0d43930c93a322f415dc75b4c5c..ca626f7fc68611de05319d78ddcc16e94b3375e1 100644 (file)
@@ -52,7 +52,7 @@ OPTIONS
 -s <format>::
 --suffix <format>::
        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-diagnose::
@@ -60,7 +60,7 @@ OPTIONS
        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
index 6e43eb9c205371548655c5abae5e59bb963c959a..0c07720c6f4e4a53a20a9db8c210143e881a2347 100644 (file)
@@ -311,7 +311,7 @@ or `--mirror` is given)
        The result is Git repository can be separated from working
        tree.
 
---ref-format=<ref-format::
+--ref-format=<ref-format>::
 
 Specify the given ref storage format for the repository. The valid values are:
 +
index c8dbceba014639524ccf2b7b1897190a9fc3c0a0..903b16830ea22529f476f9fadbe0c7aac4b31c12 100644 (file)
@@ -13,7 +13,7 @@ SYNOPSIS
 '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
index b1caac887ae2dc5ce683ab4c6d91eed849403afe..dff39093b5ef322ce115afc8f4bee0c728b6173a 100644 (file)
@@ -103,11 +103,11 @@ OPTIONS
        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::
index cf4a5a283ecd68f49fb3051b2f27007074eac4ef..4c475efeab976aefafa3dc64d41d3dcf303f63b6 100644 (file)
@@ -197,7 +197,7 @@ allowing access over SSH.
 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 @@ the database to work reliably (otherwise you need to make sure
 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
index e064f91c9e35899abd70f78a6f37158a0794f113..ede7b935d649472534dcc1bd9258c1d12231ca32 100644 (file)
@@ -18,7 +18,7 @@ SYNOPSIS
             [--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 @@ OPTIONS
        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.
@@ -141,8 +141,8 @@ otherwise `stderr`.
        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::
index 3ec8cc7ad72374b9c82db916b95ac0f6d56d5f32..0711959e6f6eaa6af5b8931058760b47189e6ba5 100644 (file)
@@ -45,7 +45,7 @@ OPTIONS
 -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.
 
index 50cb080085e7708eb117e58fc42920c710452b31..a616f8b2e6f349d4fa8f7454c5ba48a219719842 100644 (file)
@@ -90,7 +90,7 @@ instead.  `--no-symlinks` is the default on Windows.
 --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::
@@ -105,7 +105,6 @@ instead.  `--no-symlinks` is the default on Windows.
        `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.
index 4643ddbe68fd0c0eeb541df356ca3fc2bd2ee9d4..752e4b9b01d7d8a5527d1fe9a615ad8493e3f4bf 100644 (file)
@@ -48,7 +48,7 @@ When asking to 'abort' (which is the default), this program will die
 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::
index bd7b1e0a2eaf3ebf595475580d81d6771b8b4166..b2607366b91458aabefc5f2e4814959a5ba9555c 100644 (file)
@@ -745,11 +745,11 @@ paths for a commit are encouraged to do so.
 
 `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.
index f123139c581001d2bcbd94dcc187775e58e8fd6c..50900a50dabce9cd5fcee479461c42466359cad5 100644 (file)
@@ -186,8 +186,8 @@ origin:
 ------------------------------------------------
 $ 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
index 62e482a95e23fd87aa0bc5496a0584a7a7285f85..5a4f853785d119884c41de7f51b4057510f1ccf3 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
        [--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 @@ listed there as reasonably possible.
 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 @@ with:
      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).
 
index be9543f6840be3c6a477fb8537d014b0f7dfba7b..c1dd12b93cfd5c44a8d3d97c78c5f2f330f7fe3f 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
                   [(--sort=<key>)...] [--format=<format>]
-                  [ --stdin | <pattern>... ]
+                  [--include-root-refs] [ --stdin | <pattern>... ]
                   [--points-at=<object>]
                   [--merged[=<object>]] [--no-merged[=<object>]]
                   [--contains[=<object>]] [--no-contains[=<object>]]
@@ -105,6 +105,9 @@ TAB %(refname)`.
        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
 -----------
 
@@ -359,9 +362,11 @@ In any case, a field name that refers to a field inapplicable to
 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).
index 414da6b73e7dc9bee177cd91227acb36c6ed070a..728bb3821c1729dffe7565fa812570c315d0f878 100644 (file)
@@ -17,10 +17,10 @@ SYNOPSIS
                   [--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 @@ SYNOPSIS
                   [--range-diff=<previous> [--creation-factor=<percent>]]
                   [--filename-max-length=<n>]
                   [--progress]
-                  [<common diff options>]
-                  [ <since> | <revision range> ]
+                  [<common-diff-options>]
+                  [ <since> | <revision-range> ]
 
 DESCRIPTION
 -----------
@@ -64,7 +64,7 @@ There are two ways to specify which commits to operate on.
    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.
 
@@ -179,9 +179,9 @@ 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::
@@ -219,9 +219,9 @@ populated with placeholder text.
        Use the contents of <file> instead of the branch's description
        for generating the cover letter.
 
---subject-prefix=<subject prefix>::
+--subject-prefix=<subject-prefix>::
        Instead of the standard '[PATCH]' prefix in the subject
-       line, instead use '[<subject prefix>]'. This can be used
+       line, instead use '[<subject-prefix>]'. This can be used
        to name a patch series, and can be combined with the
        `--numbered` option.
 +
@@ -403,7 +403,7 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
        `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
index 6486620c3d8f2a2d7a9bb623c362d02465b06be4..5a20deefd5f5dae5fb4cfbb3ccfaef4ab723f44f 100644 (file)
@@ -79,8 +79,13 @@ OPTIONS
        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 @@ default and "Indexing objects" when `--stdin` is specified.
 --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
index f65a8cd91d4bb40e986c3610b6f1c2b89c719ace..d08c7da8f495b3e19fb763535ff742585da9ce3e 100644 (file)
@@ -119,8 +119,10 @@ OPTIONS
 
 --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
@@ -298,9 +300,8 @@ traversing the directory tree and finding files to show when the
 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
@@ -322,6 +323,18 @@ top of the directory tree.  A pattern read from a file specified
 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]
index b50acace3bc367ad7b73d2da10252e6b5f2e0d07..dd388fa21d5a51d1b6c3aca1b1d18b98f69a4f88 100644 (file)
@@ -64,10 +64,13 @@ OPTIONS
        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
index 7f991a3380201fe4c9f66e9c99604dc16b467f73..dc1bf61534106ac8c9e916ba47bb7fb0f1bf97f0 100644 (file)
@@ -16,7 +16,7 @@ DESCRIPTION
 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>.
index f8310e56a85ab0f6f211d68b1886ec68ae126b9f..c9221a68ccedc828672917576d317c42f9e3647c 100644 (file)
@@ -56,7 +56,7 @@ SUBCOMMANDS
 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::
index 8a8d32161b4455feead4bd43e251e43041a24dd0..e7e725044db418845cd8ee53aed60fba2374634f 100644 (file)
@@ -607,7 +607,7 @@ The recommended way to create commits with squash markers is by using the
 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.
 +
-Settting configuration variable `rebase.autoSquash` to true enables
+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.
 +
@@ -966,10 +966,9 @@ The interactive rebase will stop when a command fails (i.e. exits with
 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"
index ec64cbff4c6529d23fcf885ed5fa6f018404952f..a929c52982ff7629fdd32df1d008a0fb824847d0 100644 (file)
@@ -10,6 +10,7 @@ SYNOPSIS
 --------
 [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 @@ actions, and in addition the `HEAD` reflog records branch switching.
 `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.
index 1dec3148348350bc6f26a53ca2add524d3f8b9bf..932a5c3ea4741c2abbfab9b7070a51fcf0b58a37 100644 (file)
@@ -35,7 +35,7 @@ OPTIONS
 -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.
 
index 4f257126e33cc779d1f407ce61cdfbde3cf9ffad..0a65460adbded52c7d699228a7b3247c5fdf7fad 100644 (file)
@@ -114,11 +114,11 @@ FORMATS
 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
 ----------------------------
index 546faf9017723581ea8e13bea6f5f90b00372403..5d83dd36da11f3a7e5294bc5825e131c81d17633 100644 (file)
@@ -9,7 +9,7 @@ git-rev-parse - Pick out and massage parameters
 SYNOPSIS
 --------
 [verse]
-'git rev-parse' [<options>] <args>...
+'git rev-parse' [<options>] <arg>...
 
 DESCRIPTION
 -----------
@@ -130,7 +130,7 @@ for another option.
        '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`
@@ -165,9 +165,9 @@ 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 +176,7 @@ If a `pattern` is given, only refs matching the given shell glob are
 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,7 +197,7 @@ respectively, and they must begin with `refs/` when applied to `--glob`
 or `--all`. If a trailing '/{asterisk}' is intended, it must be given
 explicitly.
 
---exclude-hidden=[fetch|receive|uploadpack]::
+--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`
@@ -314,17 +314,17 @@ The following options are unaffected by `--path-format`:
 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.
 
 
index cbe0208834d51f89cf9020bdf55ce96be4221ae5..568925db533879697304459d6e47fe5f48861cf1 100644 (file)
@@ -116,7 +116,7 @@ include::rerere-options.txt[]
 
 --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
@@ -149,7 +149,7 @@ 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>""'.
+unwieldy subject lines, for example 'Reapply "Reapply "<original-subject>""'.
 Please consider rewording these to be shorter and more unique.
 
 CONFIGURATION
index 30deb7fe2a4f5506745a938b76d8bbcfb504e34b..8264f8738093cf64b7e32201826fb170dd512c73 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git send-email' [<options>] <file|directory>...
-'git send-email' [<options>] <format-patch options>
+'git send-email' [<options>] <format-patch-options>
 'git send-email' --dump-aliases
 
 
@@ -138,7 +138,7 @@ Note that no attempts whatsoever are made to validate the encoding.
 
 --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.
@@ -174,7 +174,7 @@ Sending
        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.
 
@@ -269,7 +269,7 @@ must be used for each option.
        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).
 
@@ -313,7 +313,7 @@ Automating
        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
@@ -348,19 +348,19 @@ Automating
 
 --[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>::
@@ -384,7 +384,7 @@ Automating
 - '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.
 
@@ -471,7 +471,7 @@ Information
        Instead of the normal operation, dump the shorthand alias names from
        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.
+       See 'sendemail.aliasesFile' for more information about aliases.
 
 
 CONFIGURATION
index 10fecc51a75d4783c68565d25302c50480d626ce..4dbb88373bcddadde2560e8159ef31d1f3580c58 100644 (file)
@@ -309,7 +309,7 @@ Line                                     Notes
 ------------------------------------------------------------
 # 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.
 ------------------------------------------------------------
@@ -502,7 +502,7 @@ results, so it could be faster on subsequent runs.
        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
index 695730609aa3ab5a18d45cbff6aba5d1499dfdce..ca0347a37b554d168c918c7144a28e2b0c922c1c 100644 (file)
@@ -136,7 +136,7 @@ If you really want to remove a submodule from the repository and commit
 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
@@ -185,7 +185,7 @@ submodule with the `--init` option.
 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.
 --
index 4e92308e85db5aab3b3e7f591e0ddb1f21afe724..43c68c2ec44f6a055ade52f15c29e36dfed42b45 100644 (file)
@@ -37,12 +37,12 @@ COMMANDS
        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 @@ ADVANCED OPTIONS
        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"
 
index d42efb3112787f943559723b9b19915df8181d5e..5fe519c31ec478e205223dbed919254a7ed4c610 100644 (file)
@@ -224,7 +224,7 @@ it in the repository configuration as follows:
 
 -------------------------------------
 [user]
-    signingKey = <gpg-key_id>
+    signingKey = <gpg-key-id>
 -------------------------------------
 
 `pager.tag` is only respected when listing tags, i.e., when `-l` is
index da4e8d1303a07c85964acfbd564ed4c521140cbb..e6b766d5c3ab54e6fc97d3cf31f9093a6eac5a41 100644 (file)
@@ -174,8 +174,17 @@ If you just want to run git as if it was started in `<path>` then use
        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 @@ If you just want to run git as if it was started in `<path>` then use
        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
@@ -842,7 +851,7 @@ of the SID and an optional counter (to avoid filename
 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`.
 +
@@ -872,6 +881,10 @@ for full details.
        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,
@@ -893,6 +906,11 @@ for full details.
        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
index 201bdf5edbd1badb08e73d32062b1c61ce38a4c3..4338d023d9a313edae0bae8b2ac8087a3d15a003 100644 (file)
@@ -1137,11 +1137,11 @@ The `merge.*.name` variable gives the driver a human-readable
 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
@@ -1159,8 +1159,9 @@ When left unspecified, the driver itself is used for both
 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`
 ^^^^^^^^^^^^^^^^^^^^^^
index 3cda2e07c24a96af8ff1c08c46780b5fdb490f4f..642c51227b5a0b1990c374bac4f815dac0b04c22 100644 (file)
@@ -245,20 +245,20 @@ diffcore-pickaxe: For Detecting Addition/Deletion of Specified String
 
 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
index 0773e5c3800392b8d4a548519b70379501207054..145cace1fe9fd3a583aaa7c0f605b64b78ef7554 100644 (file)
@@ -386,8 +386,8 @@ The remaining data of each directory block is grouped by type:
        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
 
index 883982e7a0516204ddd2f2f9e14775fa17e2a810..37f91d5b50ca3ac9053bd687ee540ca87de21083 100644 (file)
@@ -243,7 +243,7 @@ named remote is not being used both values will be the same.
 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 @@ hook would receive a line like the following:
   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.
 
index c2213bb77b380a213c1d4a8f6230790b9d8ecb57..35b39960296b69531fff972b6f55c39ed066a655 100644 (file)
@@ -8,7 +8,7 @@ gitk - The Git repository browser
 SYNOPSIS
 --------
 [verse]
-'gitk' [<options>] [<revision range>] [--] [<path>...]
+'gitk' [<options>] [<revision-range>] [--] [<path>...]
 
 DESCRIPTION
 -----------
@@ -124,7 +124,7 @@ gitk-specific options
        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>::
index d6c6effc2151ffa136f0f2e36a07b659e25c43e4..2cf7735be479e6863f4e373bd7cf7794624e0094 100644 (file)
@@ -378,7 +378,7 @@ fetch-pack may send "filter" commands to request a partial clone
 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
index 836b3490ccda97f6fb8356b25244365198a8f681..ec40a550ccab88b35f826e9133635e15c0f6c5ff 100644 (file)
@@ -391,14 +391,14 @@ C: Start a queue, `c_pending`, ordered by commit time (popping newest
 
 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
 
@@ -512,7 +512,7 @@ Within the command portion of the request body clients SHOULD send
 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)
index 8c1e7c61eac751d352b9268ff2d57b357ce62ed4..0b800abd567a49b24be7c9feade0596ff758df92 100644 (file)
@@ -199,7 +199,7 @@ which can be used to limit the refs sent from the server.
 
 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 @@ addition of future extensions.
 
 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:
 
@@ -363,7 +363,7 @@ 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.
 
-    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 +534,7 @@ with objects using hash algorithm X.  If not specified, the server is assumed to
 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
index 8400d591da0e8a93035d72a947b07728055eeebd..f7b5a25a0caa91c7eec2612d56300af08718c2a6 100644 (file)
@@ -151,7 +151,7 @@ the superproject's `$GIT_DIR/config` file, so the superproject's history
 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
@@ -229,7 +229,7 @@ Workflow for a third party library
   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"
 
index 59fc1d27419f534edd42968c41ec83f00ff3647e..85983587fcffa8a07daf452adc3967d85804a9d4 100644 (file)
@@ -343,7 +343,7 @@ $home_link_str::
        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 @@ Many gitweb features can be enabled (or disabled) and configured using the
 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 @@ Some features cannot be overridden per project.  For those
 features the structure of appropriate `%feature` hash element has a simpler
 form:
 ----------------------------------------------------------------------
-"<feature_name>" => {
+"<feature-name>" => {
        "override" => 0,
        "default" => [ <options>... ]
 },
index ddd4a0fc70571b83ab6c00bbfc4fd94cabfd4464..56d24a30a3f7b9ccd7046a1d66abd1cee2525ba6 100644 (file)
@@ -305,7 +305,7 @@ pathnames.  In most general form such path_info (component) based gitweb URL
 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>
 -----------------------------------------------------------------------
 
 
index f7d98c11e3dd0821094ae82cb488606a0de14732..d71b199955e15578c1392448cc5db0ce01240766 100644 (file)
@@ -638,6 +638,20 @@ The most notable example is `HEAD`.
        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
index d1a4c468e6354e47f52579d6e44f2387b51ba811..befa86d69278b480610c6d0472c005ad6093cf83 100644 (file)
@@ -177,7 +177,8 @@ Instead of `--tool=vimdiff`, you can also use one of these other variants:
 
 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:
index 1a65cac468b0f6cb008fd3fe9f1a85cc9dd6e770..14fff8a9c60ec94876ec5b330e9281845dd27a47 100644 (file)
@@ -1 +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.
index 9ce7a5eedc7c0df6ae45dde3354768fda7e5d1dd..408d9314d0887969a8cb485b59265a3c4b570ced 100644 (file)
@@ -950,10 +950,10 @@ ifdef::git-rev-list[]
 +
 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.
@@ -1022,6 +1022,10 @@ Unexpected missing objects will raise an error.
 +
 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
index 27be3741e6040ed9f9915f504793b4ad978d40ad..47281420fc4a0c901d60b2854a8f0a6e8f70587a 100644 (file)
@@ -103,5 +103,6 @@ GIT_COMMON_DIR/worktrees/<id>/config.worktree)
 
 ==== `refStorage`
 
-Specifies the file format for the ref database. The only valid value
-is `files` (loose references with a packed-refs file).
+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).
index 3985b6d3c2956f7d0b2c5d16d262a5203afe680e..06f19533134f9d8018b1b1ae924370660ec36d73 100644 (file)
@@ -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.
index 4e79c1589ece05ff21a144e5b48829a5dc39b50f..ce671f812d4ce32b156361333155f459c9521647 100644 (file)
@@ -73,8 +73,8 @@ 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>
+       [url "<actual-url-base>"]
+               insteadOf = <other-url-base>
 ------------
 
 For example, with this:
@@ -92,8 +92,8 @@ 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>
+       [url "<actual-url-base>"]
+               pushInsteadOf = <other-url-base>
 ------------
 
 For example, with this:
index 5d32ff23844108f5a388ecd35f7a38d5286310eb..6433903491054874c826880ea46e0fbc1fdb02bd 100644 (file)
@@ -4100,8 +4100,8 @@ 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
index 0901e1bb235c0419a628819c4f6ec8158d121899..df788c764b7bb4cd61a1b2a6da277bfce82cdfca 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.43.GIT
+DEF_VER=v2.44.GIT
 
 LF='
 '
index 15990ff3122eb61676e4bc39477804e77d04367b..4e255c81f22386389c7460d8f5e59426673b5a5a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -752,6 +752,10 @@ SCRIPTS = $(SCRIPT_SH_GEN) \
 
 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-date.o
 FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
@@ -762,7 +766,7 @@ fuzz-objs: $(FUZZ_OBJS)
 # 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,7 +796,6 @@ TEST_BUILTINS_OBJS += test-chmtime.o
 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-delta.o
 TEST_BUILTINS_OBJS += test-dir-iterator.o
@@ -828,7 +831,6 @@ TEST_BUILTINS_OBJS += test-partial-clone.o
 TEST_BUILTINS_OBJS += test-path-utils.o
 TEST_BUILTINS_OBJS += test-pcre2-config.o
 TEST_BUILTINS_OBJS += test-pkt-line.o
-TEST_BUILTINS_OBJS += test-prio-queue.o
 TEST_BUILTINS_OBJS += test-proc-receive.o
 TEST_BUILTINS_OBJS += test-progress.o
 TEST_BUILTINS_OBJS += test-reach.o
@@ -1124,6 +1126,7 @@ LIB_OBJS += reflog.o
 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
@@ -1342,6 +1345,8 @@ 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
@@ -1586,7 +1591,7 @@ endif
 
 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
@@ -1606,7 +1611,7 @@ else
        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 =
@@ -1642,7 +1647,7 @@ else
        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
@@ -1655,7 +1660,7 @@ IMAP_SEND_LDFLAGS += $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
 
 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
 
@@ -1663,7 +1668,7 @@ ifndef NO_OPENSSL
        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
@@ -1690,7 +1695,7 @@ ifndef NO_ICONV
        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
@@ -3676,14 +3681,14 @@ cocciclean:
        $(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) $(UNIT_TEST_PROGS)
+       $(RM) $(TEST_PROGRAMS)
        $(RM) $(FUZZ_PROGRAMS)
        $(RM) $(SP_OBJ)
        $(RM) $(HCC)
@@ -3850,23 +3855,22 @@ cover_db_html: cover_db
 #
 # 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_BIN):
-       @mkdir -p $(UNIT_TEST_BIN)
-
-$(UNIT_TEST_PROGS): $(UNIT_TEST_BIN)/%$X: $(UNIT_TEST_DIR)/%.o $(UNIT_TEST_DIR)/test-lib.o $(GITLIBS) GIT-LDFLAGS $(UNIT_TEST_BIN)
+$(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)
 
index 2c3de2f9c803798e9eb3bc31184b3d86ba69d60b..665ce5f5a83647619fba9157fa9b0141ae8b228b 100644 (file)
--- a/README.md
+++ b/README.md
@@ -39,8 +39,8 @@ Those wishing to help with error message, usage and informational message
 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/>,
 <https://marc.info/?l=git> and other archival sites.
 
index a55478f9ad4b2cd26617696780296a7b4afc204a..ae702771094d287a5dd6b4e183de5d80f46c2686 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.44.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.45.0.txt
\ No newline at end of file
index 79eda168ebb7cdb739720c2c0d16a44484522822..68f525b35cfe650ebf440a1d879a761d55aa02e6 100644 (file)
@@ -1729,14 +1729,6 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
        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
index 50c79443ba749fc56437806c21a20755c1988714..4ba64ee5b2d315eeeaa6143f5c8c2053d066a830 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -33,52 +33,57 @@ static const char *advise_get_color(enum color_advice ix)
        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_DIVERGING]                              = { "diverging", 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_WORKTREE_ADD_ORPHAN]                    = { "worktreeAddOrphan", 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_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_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[] =
@@ -118,13 +123,13 @@ void advise(const char *advice, ...)
 
 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, ...)
@@ -135,7 +140,8 @@ void advise_if_enabled(enum advice_type type, const char *advice, ...)
                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);
 }
 
@@ -164,7 +170,9 @@ int git_default_advice_config(const char *var, const char *value)
        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;
        }
 
index 2affbe142616de0d329c9aaaaee9ca355fb73adf..7d0a821f5cb8a1818154852bbb76d7e15d4fe97b 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -10,18 +10,18 @@ struct string_list;
  * 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_DIVERGING,
-       ADVICE_SUGGEST_DETACHING_HEAD,
        ADVICE_FETCH_SHOW_FORCED_UPDATES,
+       ADVICE_FORCE_DELETE_BRANCH,
        ADVICE_GRAFT_FILE_DEPRECATED,
        ADVICE_IGNORED_HOOK,
        ADVICE_IMPLICIT_IDENTITY,
@@ -32,23 +32,25 @@ struct string_list;
        ADVICE_PUSH_NEEDS_FORCE,
        ADVICE_PUSH_NON_FF_CURRENT,
        ADVICE_PUSH_NON_FF_MATCHING,
+       ADVICE_PUSH_REF_NEEDS_UPDATE,
        ADVICE_PUSH_UNQUALIFIED_REF_NAME,
-       ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
        ADVICE_PUSH_UPDATE_REJECTED,
-       ADVICE_PUSH_REF_NEEDS_UPDATE,
+       ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
        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,
 };
 
diff --git a/apply.c b/apply.c
index 7608e3301ca0727dcfec0a579ed46390afa41ab0..432837a674c3cc559f762aa4b7766bd8f43177e0 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -77,7 +77,8 @@ static int parse_whitespace_option(struct apply_state *state, const char *option
                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);
@@ -2219,7 +2220,8 @@ static void reverse_patches(struct patch *p)
                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);
@@ -3777,8 +3779,17 @@ static int check_preimage(struct apply_state *state,
                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;
index f2a0ed77523b457fa17497498d3ffb4499773a1f..8ae30125f84c463118d70b69eae88d4d21d31905 100644 (file)
@@ -365,7 +365,7 @@ static struct archiver *find_tar_filter(const char *name, size_t len)
        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;
index f1273c787d9667d81215ba0dcd32ede3319138ea..f75e50c339764db8a6b9f2266281541203d4e76f 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -158,6 +158,9 @@ static void show_list(const char *debug, int counted, int nr,
                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' : ' ',
index 534594f7f8006d11ebb9b8d10712b6fc6191a043..6719a181bd1f03af21b92d8be71a93142ef700e7 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -817,8 +817,9 @@ void remove_merge_branch_state(struct repository *r)
        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)
index ada7719561f0ec324238e613a9788a5f5a2981eb..393c10cbcf6315efb525b38db26e218bf6b1959d 100644 (file)
@@ -115,7 +115,7 @@ static int refresh(int verbose, const struct pathspec *pathspec)
        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);
index 0a32d1b6c851d4cf07bb02be9376d61a8d38e5d3..cfb63cce5fb9dff64106907947d0df25a2c25489 100644 (file)
@@ -24,6 +24,7 @@
 #include "ref-filter.h"
 #include "worktree.h"
 #include "help.h"
+#include "advice.h"
 #include "commit-reach.h"
 
 static const char * const builtin_branch_usage[] = {
@@ -190,9 +191,10 @@ static int check_branch_commit(const char *branchname, const char *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;
index 7d4899348a387b1a07377c2d968d7574a74d219d..bbf851138ec40875367aba94bc5b5de29b8dbe77 100644 (file)
@@ -221,6 +221,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                                                                     &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))
                                        die("%s not a valid tag", oid_to_hex(&oid));
@@ -416,6 +420,8 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
 
                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;
@@ -423,8 +429,6 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
                        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)
@@ -481,6 +485,8 @@ static void batch_object_write(const char *obj_name,
 
                        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);
 
index a6e30931b5c809da06c751ba6ef43e47a5a3e0ea..15293a30134094f699d15035232bbc644277910c 100644 (file)
@@ -704,7 +704,8 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
        init_checkout_metadata(&opts.meta, info->refname,
                               info->commit ? &info->commit->object.oid : null_oid(),
                               NULL);
-       parse_tree(tree);
+       if (parse_tree(tree) < 0)
+               return 128;
        init_tree_desc(&tree_desc, tree->buffer, tree->size);
        switch (unpack_trees(1, &tree_desc, &opts)) {
        case -2:
@@ -783,9 +784,15 @@ static int merge_working_tree(const struct checkout_opts *opts,
                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
+               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)
@@ -820,7 +827,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
                                oid_to_hex(old_commit_oid));
 
                init_tree_desc(&trees[0], tree->buffer, tree->size);
-               parse_tree(new_tree);
+               if (parse_tree(new_tree) < 0)
+                       exit(128);
                tree = new_tree;
                init_tree_desc(&trees[1], tree->buffer, tree->size);
 
@@ -1224,7 +1232,9 @@ static void setup_new_branch_info_and_source_tree(
        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) &&
@@ -1238,10 +1248,15 @@ static void setup_new_branch_info_and_source_tree(
        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 = 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));
        }
 }
 
index bad1b70ce8255156cf4745aca5e120371d39642d..10e98af95002118e9e9978009264db195bd2d770 100644 (file)
@@ -738,7 +738,8 @@ static int checkout(int submodule_progress, int filter_submodules)
        tree = parse_tree_indirect(&oid);
        if (!tree)
                die(_("unable to parse commit %s"), oid_to_hex(&oid));
-       parse_tree(tree);
+       if (parse_tree(tree) < 0)
+               exit(128);
        init_tree_desc(&t, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts) < 0)
                die(_("unable to checkout working tree"));
index e80218f81f94b5fc7a3e4605527556da336bb6ff..10ff7e01668203ce1b9233a90056abb65bc75e73 100644 (file)
@@ -45,6 +45,8 @@ int cmd_column(int argc, const char **argv, const char *prefix)
        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) {
index 666ad574a46b1218def7201e581e969f90e57d48..7102ee90a0094b12560108eebe38f2637f83e701 100644 (file)
@@ -21,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,
index 65196a28278bd5afb1bbc1e68864376225868abe..a91197245f18ed7b73eee64d3d5d930ca164bb06 100644 (file)
@@ -331,7 +331,8 @@ static void create_base_index(const struct commit *current_head)
        tree = parse_tree_indirect(&current_head->object.oid);
        if (!tree)
                die(_("failed to unpack HEAD tree object"));
-       parse_tree(tree);
+       if (parse_tree(tree) < 0)
+               exit(128);
        init_tree_desc(&t, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts))
                exit(128); /* We've already reported the error, finish dying */
@@ -737,7 +738,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        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 */
@@ -841,7 +841,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                    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"));
@@ -924,9 +924,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                          " 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 ?
@@ -946,8 +945,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL)
                        status_printf(s, GIT_COLOR_NORMAL, hint_cleanup_all, comment_line_char);
                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);
 
@@ -1877,7 +1876,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                                     &oid, flags);
        }
 
-       apply_autostash(git_path_merge_autostash(the_repository));
+       apply_autostash_ref(the_repository, "MERGE_AUTOSTASH");
 
 cleanup:
        strbuf_release(&author_ident);
index 11a4d4ef1411222f3750c760b68efd10180148c0..b55bfae7d66df0cea54313f677e1a924a4a579b3 100644 (file)
@@ -708,10 +708,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        }
 
        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
@@ -719,19 +717,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                         * 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) {
@@ -760,7 +747,6 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                given_config_source.scope = CONFIG_SCOPE_COMMAND;
        }
 
-
        if (respect_includes_opt == -1)
                config_options.respect_includes = !given_config_source.file;
        else
index f18f0809f9c7f78874eaddfd869c5fc5d11831e0..4693d18cc94f09ecf760c4662753583052ebc4c6 100644 (file)
@@ -136,8 +136,7 @@ static int anonymized_entry_cmp(const void *cmp_data UNUSED,
        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;
        }
 
index 119f1a72ac6605b65937af3b7cbc651b4c695c0d..0a7a1a34765483e9f207ec26c5ca408df88c9dfc 100644 (file)
@@ -100,6 +100,7 @@ static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
 
 struct fetch_config {
        enum display_format display_format;
+       int all;
        int prune;
        int prune_tags;
        int show_forced_updates;
@@ -113,6 +114,11 @@ static int git_fetch_config(const char *k, const char *v,
 {
        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_config->prune = git_config_bool(k, v);
                return 0;
@@ -442,9 +448,8 @@ static void filter_prefetch_refspec(struct refspec *rs)
                        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);
@@ -2130,7 +2135,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        const char *bundle_uri;
        struct string_list list = STRING_LIST_INIT_DUP;
        struct remote *remote = NULL;
-       int all = 0, multiple = 0;
+       int all = -1, multiple = 0;
        int result = 0;
        int prune_tags_ok = 1;
        int enable_auto_gc = 1;
@@ -2335,11 +2340,20 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
            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 */
index 3885a9c28e149e5e4133bbf25aa557c38bd4193b..919282e12a335a5a7491b905f27dcfface0120a3 100644 (file)
@@ -20,10 +20,10 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 {
        struct ref_sorting *sorting;
        struct string_list sorting_options = STRING_LIST_INIT_DUP;
-       int icase = 0;
+       int icase = 0, include_root_refs = 0, from_stdin = 0;
        struct ref_filter filter = REF_FILTER_INIT;
        struct ref_format format = REF_FORMAT_INIT;
-       int from_stdin = 0;
+       unsigned int flags = FILTER_REFS_REGULAR;
        struct strvec vec = STRVEC_INIT;
 
        struct option opts[] = {
@@ -53,6 +53,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                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(),
        };
 
@@ -96,8 +97,11 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                filter.name_patterns = argv;
        }
 
+       if (include_root_refs)
+               flags |= FILTER_REFS_ROOT_REFS;
+
        filter.match_as_path = 1;
-       filter_and_format_refs(&filter, FILTER_REFS_ALL, sorting, &format);
+       filter_and_format_refs(&filter, flags, sorting, &format);
 
        ref_filter_clear(&filter);
        ref_sorting_release(sorting);
index a7cf94f67edf3aaace9fdbbca29778d6857d28af..f892487c9b975dd5079b86143e635c6539770981 100644 (file)
@@ -509,9 +509,7 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid
        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;
 
index 7c11d5ebef052a46cd06b411960fb1c2b3d6ac08..cb80ced6cb5c65d70859a3a6f57cff3d886084b3 100644 (file)
@@ -1543,19 +1543,18 @@ static int maintenance_register(int argc, const char **argv, const char *prefix)
 
        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);
+               free(global_config_file);
 
                if (rc)
                        die(_("unable to add '%s' value of '%s'"),
@@ -1612,18 +1611,18 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi
 
        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_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE);
-               free(user_config);
-               free(xdg_config);
+               free(global_config_file);
 
                if (rc &&
                    (!force || rc == CONFIG_NOTHING_SET))
index c8e33f977552081fcde8afc12d0aa92bfc4b5e62..982bcfc4b1dfb81d35c2af2bf6118f289ec376ef 100644 (file)
@@ -571,6 +571,8 @@ static int grep_cache(struct grep_opt *opt,
 
                        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, data, size);
 
                        hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0);
index 1ea87e01f2905ed70e3a17a847278ec2b7b68ec3..a3a37bd215d844806814d677d2c7dfa71c8f31c6 100644 (file)
@@ -24,7 +24,7 @@
 #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;
@@ -1785,8 +1785,9 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                        } 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")) {
index 3bdec53fbe58bb5edef6e4d425829a58ce87bffe..b57246da0c58c61f314cf11315a7358559650f81 100644 (file)
@@ -429,35 +429,49 @@ static int real_merge(struct merge_tree_options *o,
        struct merge_options opt;
 
        copy_merge_options(&opt, &o->merge_options);
-       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"));
-
        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 = repo_get_commit_tree(the_repository, base_commit);
-               parent1_tree = repo_get_commit_tree(the_repository, parent1);
-               parent2_tree = repo_get_commit_tree(the_repository, 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
index ebbe05033e98bc796ceeb6c70e6259b8c76c18f2..935c8a57ddbcaa3b396bdcf633f34eb29d8860c1 100644 (file)
@@ -192,8 +192,7 @@ static struct strategy *get_strategy(const char *name)
                        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);
@@ -476,7 +475,7 @@ static void finish(struct commit *head_commit,
        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);
 }
 
@@ -1315,7 +1314,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        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"),
@@ -1324,17 +1324,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                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;
        }
 
@@ -1563,13 +1563,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                }
 
                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;
                }
@@ -1655,8 +1654,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                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);
@@ -1741,7 +1739,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                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)
index 2dd1807c4e09f8dc7ff557c628fdd850dc653361..ad9930c83112585f72a49883c4c8035529363fd5 100644 (file)
@@ -15,6 +15,7 @@
 #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
@@ -155,30 +156,25 @@ static struct rev_name *create_or_update_name(struct commit *commit,
        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;
@@ -195,9 +191,10 @@ static void name_rev(struct commit *start_commit,
        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);
@@ -235,7 +232,8 @@ static void name_rev(struct commit *start_commit,
                                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,
@@ -415,7 +413,7 @@ static int name_ref(const char *path, const struct object_id *oid,
        return 0;
 }
 
-static void name_tips(void)
+static void name_tips(struct mem_pool *string_pool)
 {
        int i;
 
@@ -428,7 +426,7 @@ static void name_tips(void)
                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);
                }
        }
 }
@@ -561,6 +559,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
 
 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 };
@@ -587,6 +586,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                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);
@@ -648,7 +648,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
        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;
@@ -676,6 +676,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                                  always, allow_undefined, data.name_only);
        }
 
+       UNLEAK(string_pool);
        UNLEAK(revs);
        return 0;
 }
index e65cae0bcf702bf939dcd3ddfb2af5793e73fda0..caf20fd5bdd4b7c1cb44191d58ecbb389643b748 100644 (file)
@@ -716,9 +716,11 @@ static int append_edit(int argc, const char **argv, const char *prefix)
                struct strbuf buf = STRBUF_INIT;
                char *prev_buf = repo_read_object_file(the_repository, note, &type, &size);
 
-               if (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 && prev_buf && size)
+               if (d.buf.len && size)
                        append_separator(&buf);
                strbuf_insert(&d.buf, 0, buf.buf, buf.len);
 
index d8c2128a97928229e93212461027b700d219ec6a..329aeac80437502a49b48fe5383aa40e90cd16c3 100644 (file)
@@ -4396,6 +4396,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                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);
index 20e7db19737d5a5070068a131797042d37a936a5..1ffd863cff6701af8013130d2e2801366f4e7228 100644 (file)
@@ -261,7 +261,8 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
        cache_tree_free(&the_index.cache_tree);
        for (i = 0; i < nr_trees; i++) {
                struct tree *tree = trees[i];
-               parse_tree(tree);
+               if (parse_tree(tree) < 0)
+                       return 128;
                init_tree_desc(t+i, tree->buffer, tree->size);
        }
        if (unpack_trees(nr_trees, t, &opts))
index 995818c28d2604d8bc3435e4947f90d581a87d59..6ead9465a42c6d3c6ff7a547bb42ed9dd919dd90 100644 (file)
@@ -515,7 +515,7 @@ static int finish_rebase(struct rebase_options *opts)
        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
@@ -1254,7 +1254,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                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 "
index e36b1d619f5c075fc26617fb26972dc21d561e75..db656074857e765c8b5d5c91ab1b872de0bad66d 100644 (file)
@@ -593,21 +593,6 @@ static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
        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.
@@ -622,13 +607,14 @@ static int constant_memequal(const char *a, const char *b, size_t n)
        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;
@@ -670,7 +656,6 @@ static const char *check_nonce(const char *buf, size_t len)
                goto leave;
        }
 
-       noncelen = strlen(nonce);
        expect = prepare_push_cert_nonce(service_dir, stamp);
        if (noncelen != strlen(expect)) {
                /* This is not even the right size. */
@@ -718,35 +703,28 @@ leave:
 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;
 }
 
@@ -773,7 +751,7 @@ static void prepare_push_cert_sha1(struct child_process *proc)
                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",
index a5a4099f61a5f825d6e622572c9ee85ae563de7b..060eb3377e5d5b2e8af213aa1eec2b2decb33af4 100644 (file)
@@ -7,11 +7,15 @@
 #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" \
@@ -29,6 +33,11 @@ static const char *const reflog_show_usage[] = {
        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
@@ -46,6 +55,7 @@ static const char *const reflog_exists_usage[] = {
 
 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,
@@ -60,8 +70,7 @@ struct worktree_reflogs {
        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;
@@ -96,8 +105,7 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
                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);
@@ -239,6 +247,29 @@ static int cmd_reflog_show(int argc, const char **argv, const char *prefix)
        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 };
@@ -418,6 +449,7 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)
        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),
index 8390bfe4c487b0c3170045f0601234c1e8278122..1d62ff6332737c6449c76f66d2ebcbadb36bfb11 100644 (file)
@@ -116,6 +116,10 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
 
        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);
        }
 
@@ -281,7 +285,9 @@ static void parse_args(struct pathspec *pathspec,
                        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 |
index b3f47838580c9dd0ed233a3ac34c19aac6b7fa0f..ec455aa97277dd58d106c10623ad36aa923bcd10 100644 (file)
@@ -545,6 +545,18 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
         *
         * 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")) {
@@ -753,8 +765,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
        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,
index b7183be9709fd40a54162685825166945935e522..3df9eaad092babdbbfc47f1c5441fc43a4ccd182 100644 (file)
@@ -333,6 +333,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        }
 
        if (!ret && !transport_refs_pushed(remote_refs))
+               /* stable plumbing output; do not modify or localize */
                fprintf(stderr, "Everything up-to-date\n");
 
        return ret;
index aaa2c39b2f636c6a031dc98f237c83d59b754f31..1c15421e6008e80b1983c168592998f17667bf58 100644 (file)
@@ -172,7 +172,7 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts,
        while (*refs) {
                struct object_id oid;
 
-               if ((starts_with(*refs, "refs/") || !strcmp(*refs, "HEAD")) &&
+               if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) &&
                    !read_ref(*refs, &oid)) {
                        show_one(show_one_opts, *refs, &oid);
                }
@@ -238,7 +238,7 @@ static int cmd_show_ref__exists(const char **refs)
        if (refs_read_raw_ref(get_main_ref_store(the_repository), ref,
                              &unused_oid, &unused_referent, &unused_type,
                              &failure_errno)) {
-               if (failure_errno == ENOENT) {
+               if (failure_errno == ENOENT || failure_errno == EISDIR) {
                        error(_("reference does not exist"));
                        ret = 2;
                } else {
index b2813c614cb2cf084ab28b48c1099209865e36b7..7fb355bff0a628d1d7a1a08646175ac33d8fe9d7 100644 (file)
@@ -520,7 +520,7 @@ static void unstage_changes_unless_new(struct object_id *orig_tree)
        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,
@@ -537,7 +537,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
        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))
@@ -1364,7 +1364,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
        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;
        }
 
@@ -1555,7 +1555,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 
        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;
        }
 
index f036cf32f5fafffba426e7e050f93ca39efc0eb2..19a7e06bf414ccf811c93f0ac941a142a7fabef2 100644 (file)
@@ -153,7 +153,7 @@ static int verify_tag(const char *name, const char *ref UNUSED,
 
 static int do_sign(struct strbuf *buffer)
 {
-       return sign_buffer(buffer, buffer, get_signing_key());
+       return sign_buffer(buffer, buffer, get_signing_key()) ? -1 : 0;
 }
 
 static const char tag_template[] =
@@ -530,7 +530,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                        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);
index 8cf7dd9e2e5cef1d9238c22ed64ea07440d31528..cf5567208a277a62b872a1846919060fb6e276c3 100644 (file)
@@ -90,7 +90,7 @@ static char *git_config_val_global(int ident_flag UNUSED)
        char *user, *xdg;
        size_t unused;
 
-       git_global_config(&user, &xdg);
+       git_global_config_paths(&user, &xdg);
        if (xdg && *xdg) {
                normalize_path_copy(xdg, xdg);
                strbuf_addf(&buf, "%s\n", xdg);
index cac83a94197dd60054d5266de8cc1b5186ea2b35..9c76b62b02da037db84ef633100cc6d9136eb0cf 100644 (file)
@@ -416,7 +416,6 @@ static int add_worktree(const char *path, const char *refname,
        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;
@@ -424,7 +423,8 @@ static int add_worktree(const char *path, const char *refname,
        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");
@@ -495,20 +495,32 @@ static int add_worktree(const char *path, const char *refname,
        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.
@@ -526,22 +538,6 @@ static int add_worktree(const char *path, const char *refname,
 
        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 && commit) {
-               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)
-               goto done;
 
        if (opts->orphan &&
            (ret = make_worktree_orphan(refname, opts, &child_env)))
@@ -587,6 +583,7 @@ done:
        strbuf_release(&sb_git);
        strbuf_release(&sb_name);
        strbuf_release(&realpath);
+       free_worktree(wt);
        return ret;
 }
 
@@ -851,21 +848,21 @@ static int add(int ac, const char **av, const char *prefix)
                const char *s = worktree_basename(path, &n);
                new_branch = xstrndup(s, n);
        } else if (opts.orphan) {
-               // No-op
+               ; /* no-op */
        } else if (opts.detach) {
-               // Check HEAD
+               /* 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.
+               /* 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.
+               /* DWIM: Guess branch name from path. */
                const char *s = dwim_branch(path, &new_branch);
                if (s)
                        branch = s;
 
-               // DWIM: Infer --orphan when repo has no refs.
+               /* 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;
index 64678fe19930402b8400cb05d3ccaad5bb5b00a6..80ca8324773d2dc352bf4d47cfc4d0036316180e 100644 (file)
@@ -778,8 +778,8 @@ static void prime_cache_tree_rec(struct repository *r,
                        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();
 
index 4f407530d3057552177883ad6b088f0a65b37b9a..b4e22de3cb9c351098cc57105c12e6242d1a4384 100755 (executable)
@@ -37,15 +37,13 @@ macos-*)
        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
index 48c43f0f90774e65fcc8b388c6a52444fde50506..eb2c9e1eca73d3beb19ad0d20087f50af13fc62a 100755 (executable)
@@ -21,7 +21,7 @@ linux-musl)
                apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
                bash cvs gnupg perl-cgi perl-dbd-sqlite >/dev/null
        ;;
-linux-*)
+linux-*|StaticAnalysis)
        # Required so that apt doesn't wait for user input on certain packages.
        export DEBIAN_FRONTEND=noninteractive
 
@@ -31,6 +31,11 @@ linux-*)
                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 &&
index c749b21366b95092b144611dd9182b30bf3cb4a8..0a73fc7bd1c2036afc3b7b9df169746133a8b537 100755 (executable)
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -252,7 +252,14 @@ then
        CI_COMMIT="$CI_COMMIT_SHA"
        case "$CI_JOB_IMAGE" in
        macos-*)
-               CI_OS_NAME=osx;;
+               # 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;;
        *)
@@ -344,6 +351,9 @@ macos-*)
        then
                MAKEFLAGS="$MAKEFLAGS APPLE_COMMON_CRYPTO_SHA1=Yes"
        fi
+
+       P4_PATH="$HOME/custom/p4"
+       export PATH="$P4_PATH:$PATH"
        ;;
 esac
 
@@ -357,7 +367,7 @@ linux-musl)
        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
index c33ad4e3a2220a97f6e0708afe915010f4f44561..b1f80aeac345dd70746b02b6ca1b5282a0c2a4aa 100755 (executable)
@@ -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
diff --git a/ci/run-build-and-minimal-fuzzers.sh b/ci/run-build-and-minimal-fuzzers.sh
new file mode 100755 (executable)
index 0000000..8ba486f
--- /dev/null
@@ -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 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
index 7a1466b8687f6b24ec370f6577c930cf86621cf8..c192bd613c03dd5faa69dba835014d54bfdf16ae 100755 (executable)
@@ -37,6 +37,9 @@ linux-clang)
 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.
index ff2f0abf39930c85a515283625a98152d85bc492..50bbccc92ee86cf754a6b3250e52338a1870c990 100644 (file)
--- a/column.c
+++ b/column.c
@@ -182,6 +182,8 @@ void print_columns(const struct string_list *list, unsigned int colopts,
 {
        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);
@@ -361,6 +363,8 @@ int run_column_filter(int colopts, const struct column_options *opts)
 {
        struct strvec *argv;
 
+       if (opts && (0 > opts->padding))
+               BUG("padding must be non-negative");
        if (fd_out != -1)
                return -1;
 
index db94581f72443c28fd47535db78e39b5ad021a86..d6d6fa168942053e756317d8b490435e4d1280b6 100644 (file)
@@ -337,6 +337,8 @@ static char *grab_blob(struct repository *r,
                free_filespec(df);
        } else {
                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));
        }
index f86c5e9f942f3bc5d2f0341a8ee8d2f6a6cd0dde..45417d7412202e54d21de0ae49941536eb2c82b4 100644 (file)
@@ -2619,19 +2619,16 @@ cleanup:
        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]);
+       for (i = 0; i < ctx->num_commit_graphs_before; i++)
+               free(ctx->commit_graph_filenames_before[i]);
+       free(ctx->commit_graph_filenames_before);
 
-               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);
 
index 10dbb65937d17cab381bc1bfab1dff099157a625..e9ad9db84f22797e745d7a6c4e77021c3cf1814c 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef COMPILER_H
 #define COMPILER_H
 
-#include "git-compat-util.h"
 #include "strbuf.h"
 
 #ifdef __GLIBC__
index 6c979c27d89ec858b849f87e64505c2554cb121b..23bc1bef86c87a0853b87d75e0fc35ea1932f99d 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef COMPAT_DISK_H
 #define COMPAT_DISK_H
 
-#include "git-compat-util.h"
 #include "abspath.h"
 #include "gettext.h"
 
index 42053c1f656bd8b73696b751f30177ce9e544d59..320fb99a90e1db6006135e9798f7300f6fa29798 100644 (file)
@@ -707,13 +707,24 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
 {
        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;
@@ -2684,6 +2695,30 @@ static PSID get_current_user_sid(void)
        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);
@@ -2765,27 +2800,47 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
                        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);
                }
        }
 
index 9ff6ae1cb903a0690295a7d5bb452ef5f35c12e1..3cfeb3d8bd99f4ca15d0f3a06cd4b1fe932f7f47 100644 (file)
--- a/config.c
+++ b/config.c
@@ -95,7 +95,6 @@ static long config_file_ftell(struct config_source *conf)
        return ftell(conf->u.file);
 }
 
-
 static int config_buf_fgetc(struct config_source *conf)
 {
        if (conf->u.buf.pos < conf->u.buf.len)
@@ -1988,7 +1987,27 @@ char *git_system_config(void)
        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;
@@ -2041,7 +2060,7 @@ static int do_git_config_sequence(const struct config_options *opts,
                                                         data, CONFIG_SCOPE_SYSTEM,
                                                         NULL);
 
-       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_with_options(fn, xdg_config, data,
@@ -3418,7 +3437,6 @@ out_free:
 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,
index 14f881ecfaf6b264b6d120205ab509f2ee5930f2..5dba984f770e4e96d322874351a70d0f7d0ee8ba 100644 (file)
--- a/config.h
+++ b/config.h
@@ -382,7 +382,8 @@ int config_error_nonbool(const char *);
 #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);
 
index 3bb03f423a08102f837c52dcbe7a3b581104e0cb..dacc95172dcdcbd85086d9dacf57d2be588c90e1 100644 (file)
@@ -158,6 +158,19 @@ ifeq ($(uname_S),Darwin)
                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
diff --git a/contrib/coccinelle/xstrncmpz.cocci b/contrib/coccinelle/xstrncmpz.cocci
new file mode 100644 (file)
index 0000000..ccb39e2
--- /dev/null
@@ -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)
+)
index 8c40ade49411bb883799d79983ad33a696651aa8..fcf1afd75de4eb14a0123a2a75692298f2c39a0b 100644 (file)
@@ -137,6 +137,9 @@ __git_eread ()
 __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
@@ -144,9 +147,8 @@ __git_pseudoref_exists ()
        # Bash builtins since executing Git commands are expensive on some
        # platforms.
        if __git_eread "$__git_repo_path/HEAD" head; then
-               b="${head#ref: }"
-               if [ "$b" == "refs/heads/.invalid" ]; then
-                       __git -C "$__git_repo_path" rev-parse --verify --quiet "$ref" 2>/dev/null
+               if [ "$head" == "ref: refs/heads/.invalid" ]; then
+                       __git show-ref --exists "$ref"
                        return $?
                fi
        fi
@@ -1481,12 +1483,32 @@ _git_bisect ()
 {
        __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
@@ -1494,7 +1516,26 @@ _git_bisect ()
        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
                ;;
        *)
@@ -1656,7 +1697,6 @@ __git_cherry_pick_inprogress_options=$__git_sequencer_inprogress_options
 
 _git_cherry_pick ()
 {
-       __git_find_repo_path
        if __git_pseudoref_exists CHERRY_PICK_HEAD; then
                __gitcomp "$__git_cherry_pick_inprogress_options"
                return
@@ -1807,7 +1847,7 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
                        --output= --output-indicator-context=
                        --output-indicator-new= --output-indicator-old=
                        --ws-error-highlight=
-                       --pickaxe-all --pickaxe-regex
+                       --pickaxe-all --pickaxe-regex --patch-with-raw
 "
 
 # Options for diff/difftool
@@ -2071,6 +2111,16 @@ __git_log_common_options="
        --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="
@@ -2086,6 +2136,7 @@ __git_log_shortlog_options="
 # 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"
@@ -2093,10 +2144,12 @@ __git_diff_merges_opts="off none on first-parent 1 separate m combined c dense-c
 __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 __git_pseudoref_exists MERGE_HEAD; then
@@ -2169,6 +2222,8 @@ _git_log ()
                        --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
                        "
@@ -2190,6 +2245,16 @@ _git_log ()
                return
                ;;
        esac
+}
+
+_git_log ()
+{
+       __git_has_doubledash && return
+       __git_find_repo_path
+
+       __git_complete_log_opts
+        [ ${#COMPREPLY[@]} -eq 0 ] || return
+
        __git_complete_revlist
 }
 
@@ -2595,6 +2660,33 @@ __git_compute_config_vars ()
        __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 ()
 {
@@ -2739,73 +2831,50 @@ __git_complete_config_variable_name ()
        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
                ;;
        *.*)
@@ -2966,7 +3035,6 @@ _git_reset ()
 
 _git_restore ()
 {
-       __git_find_repo_path
        case "$prev" in
        -s)
                __git_complete_refs
@@ -2995,7 +3063,6 @@ __git_revert_inprogress_options=$__git_sequencer_inprogress_options
 
 _git_revert ()
 {
-       __git_find_repo_path
        if __git_pseudoref_exists REVERT_HEAD; then
                __gitcomp "$__git_revert_inprogress_options"
                return
index 2c030050aea1c67472570a9d86d69d55e31e7714..71f179cba3fbda3bc93de461649cf75bb08c6653 100644 (file)
@@ -408,7 +408,7 @@ __git_ps1 ()
 
        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="$?"
 
@@ -421,6 +421,8 @@ __git_ps1 ()
                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'}"
@@ -479,12 +481,25 @@ __git_ps1 ()
                        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
@@ -502,6 +517,8 @@ __git_ps1 ()
 
                                b="$short_sha..."
                                b="($b)"
+                       else
+                               b="$head"
                        fi
                fi
        fi
index 215a81d8bae59ca2364eb762c72089ff8b6b51d7..90034d0cf1eb3d04c04872f38f2c8ba3a25fd904 100644 (file)
@@ -164,6 +164,9 @@ static int keyring_get(struct credential *c)
                        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=")) {
index 4cd56c42e24469a48a40e7b02de4b4921ff8662c..4be0d58cd89ad7bfbbf8c8fc0b2268cee9052f9e 100644 (file)
@@ -35,7 +35,7 @@ static void *xmalloc(size_t size)
 }
 
 static WCHAR *wusername, *password, *protocol, *host, *path, target[1024],
-       *password_expiry_utc;
+       *password_expiry_utc, *oauth_refresh_token;
 
 static void write_item(const char *what, LPCWSTR wbuf, int wlen)
 {
@@ -140,6 +140,11 @@ static void get_credential(void)
        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;
@@ -149,9 +154,24 @@ static void get_credential(void)
                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")) {
@@ -170,16 +190,26 @@ 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;
@@ -194,6 +224,8 @@ static void store_credential(void)
        cred.TargetAlias = NULL;
        cred.UserName = wusername;
 
+       free(secret);
+
        if (!CredWriteW(&cred, 0))
                die("CredWrite failed");
 }
@@ -265,6 +297,8 @@ static void read_credential(void)
                        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
index 3028029ac2ddf67608dad1f6538b37a347aebe29..5dab3f506c6e0877740f7d3990388b3975e877c5 100755 (executable)
@@ -787,6 +787,22 @@ ensure_valid_ref_format () {
                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
@@ -972,7 +988,19 @@ cmd_split () {
        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 $?
index 49a21dd7c9c553f80a905ca8333b93fbe05fcc67..ca4df5be83245fe9a37a0b46737b61071f3b78b0 100755 (executable)
@@ -385,6 +385,46 @@ test_expect_success 'split sub dir/ with --rejoin' '
        )
 '
 
+# 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 &&
index 888c34a5215258ad7d5715771f92ab58f3efd363..989197aace0bc03ecae6d5f94af86e6e91968562 100755 (executable)
@@ -79,7 +79,7 @@ trap cleanup $siglist
 # 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
index a8870baff36a4a3042baf31c730793b45f6b6442..35b25eb3cb9212f92dd918d0f247e49ecd620c41 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1028,7 +1028,7 @@ static int read_convert_config(const char *var, const char *value,
        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);
diff --git a/diff.c b/diff.c
index a89a6a6128ad68de5d29d47061386f21d91a2337..e50def45383eba4af74300802a161bdeeb6c4e8f 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -4384,7 +4384,8 @@ static void run_external_diff(const char *pgm,
                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);
                }
        }
 
@@ -5589,7 +5590,7 @@ struct option *add_diff_options(const struct option *opts,
                OPT_BITOP(0, "shortstat", &options->output_format,
                          N_("output only the last line of --stat"),
                          DIFF_FORMAT_SHORTSTAT, DIFF_FORMAT_NO_OUTPUT),
-               OPT_CALLBACK_F('X', "dirstat", options, N_("<param1,param2>..."),
+               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),
@@ -5597,8 +5598,8 @@ struct option *add_diff_options(const struct option *opts,
                               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,
index 4927ab8fb0c91554b849e3046e424cccc7eff325..ba6cbee76ba0180db352d4baf0653af201304046 100644 (file)
@@ -158,6 +158,10 @@ static struct spanhash_top *hash_chars(struct repository *r,
                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;
 }
index 278b04243a3f40e63df433eea9549f90aed1c34d..de619846f29ad9c51d42b538d12c1b62e3958b5d 100644 (file)
@@ -2,10 +2,19 @@
 #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 @@ struct dir_iterator_int {
        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 @@ static int push_level(struct dir_iterator_int *iter)
                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 @@ static int pop_level(struct dir_iterator_int *iter)
                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 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
                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;
-               }
 
-               if (is_dot_or_dotdot(de->d_name))
-                       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;
+                       }
 
-               if (prepare_next_entry_data(iter, de)) {
+                       name = level->entries.items[level->entries_idx++].string;
+               }
+
+               if (prepare_next_entry_data(iter, name)) {
                        if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
                                goto error_out;
                        continue;
@@ -188,6 +259,8 @@ int dir_iterator_abort(struct dir_iterator *dir_iterator)
                        warning_errno("error closing directory '%s'",
                                      iter->base.path.buf);
                }
+
+               string_list_clear(&level->entries, 0);
        }
 
        free(iter->levels);
index 479e1ec784dfa4081430ea5a104302e79d10eae3..6d438809b6ed51b5735080f878c08aa2302b7552 100644 (file)
  *   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: */
index 90632a39bc995af8bf56166b4d89bba5d6dd5272..60706ea3987f2c4d11e5e2e2d21aa5cd5e9196e1 100644 (file)
@@ -207,6 +207,9 @@ void setup_git_env(const char *git_dir)
        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)
index e5351c9dd95ea6e7afe77b1db466d6ab30310491..5cec19cecc1a5a2c057f807f3b96dd2b29f0b355 100644 (file)
@@ -36,6 +36,7 @@ const char *getenv_safe(struct strvec *argv, const char *name);
 #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]"
index 5b8aa0adc77621504619b831bdd842b83f4867a1..091f9a80a9ee72593c5079b48f15ea22dbd59d4c 100644 (file)
@@ -2216,7 +2216,7 @@ void negotiate_using_fetch(const struct oid_array *negotiation_tips,
                                           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/fsck.c b/fsck.c
index 1ad02fcdfabea74dc7f3d1c214400516d327a421..8ded0a473a47fb45c223d8736f58c599b66cbb93 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -20,7 +20,6 @@
 #include "packfile.h"
 #include "submodule-config.h"
 #include "config.h"
-#include "credential.h"
 #include "help.h"
 
 static ssize_t max_tree_entry_len = 4096;
@@ -1047,138 +1046,6 @@ done:
        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;
index e4e820e68095928765940be51fba0cdb8e5d609c..dd0c9a5b7f2b078205000f3051df0bade8b0cfab 100755 (executable)
@@ -91,6 +91,19 @@ then
        # 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
index 0eb3bb4c47dcf12a9f1d98e1b76a3681197ac52e..28ab12c72b6561bf6f583391acf8daa300a6211b 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -689,8 +689,8 @@ def setP4ExecBit(file, mode):
 
     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 @@ def getP4OpenedType(file):
     """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 @@ def parseDiffTreeEntry(entry):
 
     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 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,
             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_encode(path):
 
 
 def wildcard_present(path):
-    m = re.search("[*#@%]", path)
+    m = re.search(r"[*#@%]", path)
     return m is not None
 
 
@@ -3048,7 +3048,7 @@ class P4Sync(Command, P4UserMap):
             # 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):]
 
@@ -3603,7 +3603,7 @@ class P4Sync(Command, P4UserMap):
                     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:
@@ -4182,7 +4182,7 @@ class P4Sync(Command, P4UserMap):
                 if len(self.changesFile) == 0:
                     revision = "#head"
 
-            p = re.sub("\.\.\.$", "", p)
+            p = re.sub(r"\.\.\.$", "", p)
             if not p.endswith("/"):
                 p += "/"
 
@@ -4251,7 +4251,8 @@ class P4Sync(Command, P4UserMap):
         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".
@@ -4291,7 +4292,7 @@ class P4Rebase(Command):
             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()
@@ -4320,8 +4321,8 @@ class P4Clone(P4Sync):
     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.c b/git.c
index 7068a184b0a2947c6d62c3331c6f1a418708c7be..5265f920f165d99f01584eed54554dd51563acca 100644 (file)
--- a/git.c
+++ b/git.c
@@ -4,6 +4,7 @@
 #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"
@@ -186,6 +187,11 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        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")) {
                        disable_replace_refs();
                        setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
index fc6d5dd522bf4f4c9869e4e14ea5a07409155784..ccd14e0e30c14ef1da31d09cf6949e70b75cf5e0 100755 (executable)
@@ -728,9 +728,11 @@ our $per_request_config = 1;
 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;
        }
index f614105033230c5f5356ff18c49bd47ff500cb35..95e764acb14b3e069a255a8261a7d724c5f8d1ad 100644 (file)
@@ -1078,7 +1078,7 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
                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;
        }
 
index 143cdc1c02d4b31fa08f92fbcae1691c80d2c2d6..7cd98161f7f21b5946daa6834617d4876745119f 100644 (file)
@@ -66,7 +66,7 @@ size_t parse_signed_buffer(const char *buf, size_t size);
  * 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);
index ff07b87e6461446610c35708a39f22a5e5b36aff..1ed1e29d0706bfe51e2074cabe393e0ed6eba7c7 100644 (file)
@@ -38,6 +38,7 @@ struct rpc_service {
 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)
@@ -639,10 +640,15 @@ static void check_content_type(struct strbuf *hdr, const char *accepted_type)
 
 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);
@@ -655,9 +661,9 @@ static void service_rpc(struct strbuf *hdr, char *service_name)
 
        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;
@@ -723,6 +729,7 @@ static struct service_cmd {
        {"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}
 };
 
index b4d0b2a6aa381f4edb1bd890de4a4be1d10ab8fb..12d111374107a7a071ac90d035c5172af2b63c79 100644 (file)
@@ -1851,6 +1851,7 @@ int cmd_main(int argc, const char **argv)
 
                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);
@@ -1871,6 +1872,7 @@ int cmd_main(int argc, const char **argv)
                                 * 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 "
index d662811ee83a3531299accb59b8acea0e4de67d8..f2e1947e63815c80e45f648f0ce37cd6341f825b 100644 (file)
@@ -28,6 +28,7 @@
 #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
index da287cc8e0dd2935cb230dd256d5f268495ed40f..4346f8da4560fd518d73db7969faa9127e302090 100644 (file)
@@ -711,15 +711,6 @@ static void filter_combine__free(void *filter_data)
        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)
@@ -728,7 +719,7 @@ static void filter_combine__finalize_omits(
        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);
        }
 }
index c7d62560201984db8efcb675da9b4ede933c5cbd..2078c22b097a61c97faddb6689a75e6c10ccb9ee 100644 (file)
@@ -107,6 +107,45 @@ void *mem_pool_alloc(struct mem_pool *pool, size_t len)
        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;
+       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);
+
+       ret = mem_pool_alloc(pool, len + 1);  /* 1 for NUL */
+
+       /* Shortcut; relies on mem_pool_alloc() not touching buffer contents. */
+       if (ret == next_free)
+               return ret;
+
+       len2 = vsnprintf(ret, len + 1, 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);
index fe7507f022bba40d74aab341e99269ead7171695..d1c66413ec322104f6199cc2248466c734cee868 100644 (file)
@@ -47,6 +47,11 @@ void *mem_pool_calloc(struct mem_pool *pool, size_t count, size_t size);
 char *mem_pool_strdup(struct mem_pool *pool, const char *str);
 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
index 1df58ebaac08582d042268f5a07cf5cbd9d7b1c3..61e0ae53981dbc786061a1b8ed7689ca5fee3912 100644 (file)
@@ -185,9 +185,9 @@ static void create_temp(mmfile_t *src, char *path, size_t len)
 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)
 {
@@ -222,6 +222,12 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,
                        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, '%');
        }
@@ -286,7 +292,7 @@ static int read_merge_config(const char *var, const char *value,
         * 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);
@@ -315,7 +321,12 @@ static int read_merge_config(const char *var, const char *value,
                 *    %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.
index 77ba7f3020c8c112bf2e6bff9dae78d4d0ca847b..817f7b57c7bee9db817b5c9c77532615aef3d160 100644 (file)
@@ -18,6 +18,7 @@
 #include "merge-ort.h"
 
 #include "alloc.h"
+#include "advice.h"
 #include "attr.h"
 #include "cache-tree.h"
 #include "commit.h"
@@ -38,6 +39,7 @@
 #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"
@@ -1656,9 +1658,10 @@ static int collect_merge_info(struct merge_options *opt,
        info.data = opt;
        info.show_all_errors = 1;
 
-       parse_tree(merge_base);
-       parse_tree(side1);
-       parse_tree(side2);
+       if (parse_tree(merge_base) < 0 ||
+           parse_tree(side1) < 0 ||
+           parse_tree(side2) < 0)
+               return -1;
        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);
@@ -2641,7 +2644,7 @@ static void apply_directory_rename_modifications(struct merge_options *opt,
                        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;
        }
 
@@ -4375,9 +4378,11 @@ static int checkout(struct merge_options *opt,
        unpack_opts.verbose_update = (opt->verbosity > 2);
        unpack_opts.fn = twoway_merge;
        unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */
-       parse_tree(prev);
+       if (parse_tree(prev) < 0)
+               return -1;
        init_tree_desc(&trees[0], prev->buffer, prev->size);
-       parse_tree(next);
+       if (parse_tree(next) < 0)
+               return -1;
        init_tree_desc(&trees[1], next->buffer, next->size);
 
        ret = unpack_trees(2, trees, &unpack_opts);
@@ -4555,7 +4560,7 @@ static void print_submodule_conflict_suggestion(struct string_list *csub) {
                      " - 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);
@@ -4659,9 +4664,6 @@ void merge_switch_to_result(struct merge_options *opt,
 {
        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 */
@@ -4687,10 +4689,17 @@ void merge_switch_to_result(struct merge_options *opt,
                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)
@@ -4977,6 +4986,9 @@ redo:
 
        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);
        }
index a0c3e7a2d9105dd895f0ae2613d0f87f00b8d794..d58c05ad2ce847b9d41fecc01c42d8af4a90b9e1 100644 (file)
@@ -405,7 +405,8 @@ static inline int merge_detect_rename(struct merge_options *opt)
 
 static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
 {
-       parse_tree(tree);
+       if (parse_tree(tree) < 0)
+               exit(128);
        init_tree_desc(desc, tree->buffer, tree->size);
 }
 
diff --git a/merge.c b/merge.c
index ca89b312d173530f8c8a1e5d07470b9d2404e9d4..563281b10f1d04bd6b51a79f2852a61aa49552fd 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -77,7 +77,10 @@ int checkout_fast_forward(struct repository *r,
                return -1;
        }
        for (i = 0; i < nr_trees; i++) {
-               parse_tree(trees[i]);
+               if (parse_tree(trees[i]) < 0) {
+                       rollback_lock_file(&lock_file);
+                       return -1;
+               }
                init_tree_desc(t+i, trees[i]->buffer, trees[i]->size);
        }
 
index 06937acbf5497115c9ba4d9a2377634b1885db7e..97e376329bf510fce8c70c89a6de7a5465190d2b 100644 (file)
@@ -371,9 +371,17 @@ diff_cmd_help () {
 
 
 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/neue b/neue
deleted file mode 100644 (file)
index e69de29..0000000
index 3a2ef5d6800173fa669bdfcb2612bf21a7c6417a..511f09bc0fea75301433fe888efbe10d5227fdc0 100644 (file)
@@ -1034,6 +1034,15 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
                                                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);
index 2c61e4c86217e633d2e28acd0b3ae654584ede7d..e6a1c4d9059ab398f06f042a7b7cdd953ff6a943 100644 (file)
--- a/object.c
+++ b/object.c
@@ -47,8 +47,7 @@ int type_from_string_gently(const char *str, ssize_t len, int gentle)
                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)
index d1e5376316ecd5f9dcf549e1067697283bdc712c..91d13859106a1bc56632aeedd4030a007608a359 100644 (file)
--- a/oidset.c
+++ b/oidset.c
@@ -23,6 +23,16 @@ int oidset_insert(struct oidset *set, const struct object_id *oid)
        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);
index ba4a5a2cd3a7a233bc9ca2cb7cf4a58a1e5a122c..262f4256d6ac5ad39e1cef4a39e36716e817d651 100644 (file)
--- a/oidset.h
+++ b/oidset.h
@@ -47,6 +47,12 @@ int oidset_contains(const struct oidset *set, const struct object_id *oid);
  */
 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.
  *
diff --git a/oss-fuzz/dummy-cmd-main.c b/oss-fuzz/dummy-cmd-main.c
new file mode 100644 (file)
index 0000000..071cb23
--- /dev/null
@@ -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;
+}
index 229a11fb00fc20257385aca4cd653c26f8ce6b31..2baeabacee13d4ec5311261499fe10d8f0a882e5 100644 (file)
@@ -51,13 +51,6 @@ struct bitmap_index {
        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 */
index 4ce2b7ca1689eeb374fa2e122e1cd2436421d405..63a99dea6ef06b19bbb679a3ddd76087abc2a028 100644 (file)
@@ -357,6 +357,7 @@ static enum parse_opt_result parse_long_opt(
        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;
+       int allow_abbrev = !(p->flags & PARSE_OPT_KEEP_UNKNOWN_OPT);
 
        for (; options->type != OPTION_END; options++) {
                const char *rest, *long_name = options->long_name;
@@ -367,12 +368,16 @@ static enum parse_opt_result parse_long_opt(
                if (!long_name)
                        continue;
 
-again:
+               if (!starts_with(arg, "no-") &&
+                   !(options->flags & PARSE_OPT_NONEG) &&
+                   skip_prefix(long_name, "no-", &long_name))
+                       opt_flags |= OPT_UNSET;
+
                if (!skip_prefix(arg, long_name, &rest))
                        rest = NULL;
                if (!rest) {
                        /* abbreviated? */
-                       if (!(p->flags & PARSE_OPT_KEEP_UNKNOWN_OPT) &&
+                       if (allow_abbrev &&
                            !strncmp(long_name, arg, arg_end - arg)) {
 is_abbreviated:
                                if (abbrev_option &&
@@ -396,22 +401,18 @@ is_abbreviated:
                        if (options->flags & PARSE_OPT_NONEG)
                                continue;
                        /* negated and abbreviated very much? */
-                       if (starts_with("no-", arg)) {
+                       if (allow_abbrev && 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;
-                               }
+                       if (!starts_with(arg, "no-"))
                                continue;
-                       }
                        flags |= OPT_UNSET;
                        if (!skip_prefix(arg + 3, long_name, &rest)) {
                                /* abbreviated and negated? */
-                               if (starts_with(long_name, arg + 3))
+                               if (allow_abbrev &&
+                                   starts_with(long_name, arg + 3))
                                        goto is_abbreviated;
                                else
                                        continue;
diff --git a/path.c b/path.c
index 67e2690efef897b406d46eab6202b52fc65a55d0..8bb223c92c91c2d963ab4fefa9efb0ac7c3b026c 100644 (file)
--- a/path.c
+++ b/path.c
@@ -871,7 +871,7 @@ const char *enter_repo(const char *path, int strict)
        return NULL;
 }
 
-static int calc_shared_perm(int mode)
+int calc_shared_perm(int mode)
 {
        int tweak;
 
@@ -1588,7 +1588,5 @@ REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
 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 639372edd9ee36127e3b89084f89ee59d12ff60f..e053effef20cbad3115095d54989cb9d4d91cfff 100644 (file)
--- a/path.h
+++ b/path.h
@@ -175,14 +175,13 @@ const char *git_path_merge_msg(struct repository *r);
 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);
index a3fc5b74d8afc62550681297e4aaa66881fd6f1f..6b95addef41e21943dbe0f72eb03ccb5683e68a1 100644 (file)
--- 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
 # ------------------------
 # midx, multi-pack index - файл с индекса за множество пакети
 # overlay mode - припокриващ режим (при изтеглянe)
 # incremental file нарастващ файл
+# commit-graph граф с подавания
+# commit-graph chain верига на гра̀фа с подавания
 # split (commit-graphr) раздробен (граф с подавания)
 # clobber (a tag) презаписвам (етикет)
 # blame извеждане на авторство
 # 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 - проверка - намират се лесно по „$
 # 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.43\n"
+"Project-Id-Version: git 2.44\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-17 15:49+0100\n"
-"PO-Revision-Date: 2023-11-18 13:23+0100\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"
@@ -1690,6 +1701,10 @@ 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“"
@@ -1737,6 +1752,14 @@ 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"
@@ -3074,12 +3097,13 @@ 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"
-"да го изтриете, изпълнете:\n"
+"Ако сте сигурни, че искате да го изтриете, изпълнете:\n"
 "\n"
 "    git branch -D %s"
 
@@ -3126,7 +3150,7 @@ msgstr "подаването, сочено от указателя „HEAD“, 
 
 #, 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"
@@ -4138,8 +4162,8 @@ msgstr "принудително изтегляне (вашите промѐн
 msgid "new-branch"
 msgstr "НОВ_КЛОН"
 
-msgid "new unparented branch"
-msgstr "нов ÐºÐ»Ð¾Ð½ Ð±ÐµÐ· Ñ\80одиÑ\82ел"
+msgid "new unborn branch"
+msgstr "нов Ð½ÐµÑ\80одеÌ\80н ÐºÐ»Ð¾Ð½"
 
 msgid "update ignored files (default)"
 msgstr "обновяване на игнорираните файлове (стандартно)"
@@ -4392,9 +4416,6 @@ msgstr ""
 "което изисква някоя от опциите „-i“, „-n“ или „-f“.  Няма да се извърши "
 "изчистване"
 
-msgid "-x and -X cannot be used together"
-msgstr "опциите „-x“ и „-X“ са несъвместими"
-
 msgid "git clone [<options>] [--] <repo> [<dir>]"
 msgstr "git clone [ОПЦИЯ…] [--] ХРАНИЛИЩЕ [ДИРЕКТОРИЯ]"
 
@@ -4486,6 +4507,9 @@ msgstr "СЛУЖЕБНА_ДИРЕКТОРИЯ"
 msgid "separate git dir from working tree"
 msgstr "отделна СЛУЖЕБНА_ДИРЕКТОРИЯ за git извън работното дърво"
 
+msgid "specify the reference format to use"
+msgstr "указване на форма̀та за указател"
+
 msgid "key=value"
 msgstr "КЛЮЧ=СТОЙНОСТ"
 
@@ -4613,12 +4637,9 @@ msgstr "Прекалено много аргументи."
 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"
@@ -4756,7 +4777,7 @@ msgid ""
 "--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|--"
@@ -7144,6 +7165,10 @@ msgstr "липсва поддръжка за нишки.  „%s“ ще се п
 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"
@@ -7565,10 +7590,6 @@ msgid "SHA1 COLLISION FOUND WITH %s !"
 msgstr ""
 "СЪВПАДЕНИЕ НА СТОЙНОСТИТЕ ЗА СУМИТЕ ЗА SHA1: „%s“ НА ДВА РАЗЛИЧНИ ОБЕКТА!"
 
-#, c-format
-msgid "unable to read %s"
-msgstr "обектът „%s“ не може да бъде прочетен"
-
 #, c-format
 msgid "cannot read existing object info %s"
 msgstr "съществуващият обект в „%s“ не може да бъде прочетен"
@@ -7711,6 +7732,7 @@ 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 ""
@@ -8449,6 +8471,14 @@ msgstr ""
 "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 "извеждане на резултатите на стандартния изход"
 
@@ -8470,6 +8500,12 @@ 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 "при конфликти да се ползва маркер с такъв БРОЙ знаци"
 
@@ -8561,9 +8597,6 @@ msgstr "„--trivial-merge“ е несъвместима с другите оп
 msgid "unknown strategy option: -X%s"
 msgstr "непозната опция за стратегия: -X%s"
 
-msgid "--merge-base is incompatible with --stdin"
-msgstr "опциите „--merge-base“ и „--stdin“ са несъвместими"
-
 #, c-format
 msgid "malformed input line: '%s'."
 msgstr "входен ред с неправилен формат: „%s“."
@@ -8606,7 +8639,7 @@ 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 "създаване на едно подаване вместо извършване на сливане"
@@ -9522,6 +9555,11 @@ 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-"
@@ -9795,10 +9833,10 @@ 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"
@@ -10791,13 +10829,6 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "опцията „C“ очаква число за аргумент"
 
-msgid ""
-"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
-"autosquash"
-msgstr ""
-"опциите за прилагане са несъвместими с „rebase.autoSquash“.  Пробвайте да "
-"добавите опцията „--no-autosquash“"
-
 msgid ""
 "apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
 "no-rebase-merges"
@@ -11823,6 +11854,76 @@ 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]"
@@ -12030,15 +12131,6 @@ msgstr "опцията „--prefix“ изисква аргумент"
 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 "тази команда трябва да се изпълни в работно дърво"
 
@@ -12242,7 +12334,7 @@ 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 [ОПЦИЯ…]"
+msgstr "git log --pretty=short | git shortlog [ОПЦИЯ…]"
 
 msgid "using multiple --group options with stdin is not supported"
 msgstr "повече от една опции „--group“ са несъвместими със стандартния вход"
@@ -12455,10 +12547,6 @@ msgstr ""
 "извеждане на указателите приети от стандартния вход, които липсват в "
 "локалното хранилище"
 
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "опциите „%s“, „%s“ и „%s“ са несъвместими"
-
 msgid ""
 "git sparse-checkout (init | list | set | add | reapply | disable | check-"
 "rules) [<options>]"
@@ -13951,7 +14039,7 @@ msgstr "Липсва клон-източник, затова се приема 
 
 #, c-format
 msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"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"
@@ -13965,7 +14053,7 @@ msgstr ""
 
 #, c-format
 msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"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"
@@ -14034,6 +14122,10 @@ 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“)"
@@ -14076,10 +14168,6 @@ msgstr ""
 "доставете\n"
 "обектите от отдалеченото хранилище"
 
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "опциите „%s“ и „%s“ са несъвместими"
-
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "Изтегляне КЛОНа, дори и да е изтеглен в друго работно дърво"
 
@@ -14089,7 +14177,7 @@ msgstr "създаване на нов клон"
 msgid "create or reset a branch"
 msgstr "създаване или зануляване на клони"
 
-msgid "create unborn/orphaned branch"
+msgid "create unborn branch"
 msgstr "създаване на неродѐн клон"
 
 msgid "populate the new working tree"
@@ -14112,11 +14200,8 @@ msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "опциите „%s“, „%s“ и „%s“ са несъвместими"
 
 #, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "опциите „%s“ и „%s“ са несъвместими"
-
-msgid "<commit-ish>"
-msgstr "ПОДАВАНЕ"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "опциите „%s“ и указателите към подавания са несъвместими"
 
 msgid "added with --lock"
 msgstr "добавена с „--lock“"
@@ -14753,6 +14838,11 @@ 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 "Обобщение на предстоящите промѐни"
 
@@ -14991,6 +15081,36 @@ msgstr "Инструмент за управление на големи хра
 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"
@@ -15007,6 +15127,19 @@ msgstr "версията на контролната сума на гра̀фа
 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 "базовият откъс липсва в гра̀фа с подаванията"
 
@@ -15020,6 +15153,9 @@ msgstr "веригата на гра̀фа с подаванията не съв
 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 ""
@@ -15038,12 +15174,16 @@ 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 ""
+"указателят за допълнителните ребра в гра̀фа с подаванията е извън позволения "
+"диапазон"
+
 msgid "Loading known commits in commit graph"
 msgstr "Зареждане на познатите подавания в гра̀фа с подаванията"
 
@@ -16244,6 +16384,10 @@ msgstr ""
 msgid "Unknown value for 'diff.submodule' config variable: '%s'"
 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"
@@ -16323,14 +16467,6 @@ msgstr "неправилен аргумент за „--color-moved“: „%s“
 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“"
@@ -16374,8 +16510,8 @@ msgstr "„--stat“ във формат за четене от програма
 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"
@@ -16384,8 +16520,8 @@ msgstr "извеждане на разпределението на промѐ
 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 ""
@@ -16569,12 +16705,6 @@ msgstr "разлика чрез алгоритъм за подредба кат
 msgid "generate diff using the \"histogram diff\" algorithm"
 msgstr "разлика по хистограмния алгоритъм"
 
-msgid "<algorithm>"
-msgstr "АЛГОРИТЪМ"
-
-msgid "choose a diff algorithm"
-msgstr "избор на АЛГОРИТЪМа за разлики"
-
 msgid "<text>"
 msgstr "ТЕКСТ"
 
@@ -18093,6 +18223,13 @@ msgid "multi-pack-index OID fanout is of the wrong size"
 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 "неправилен размер на откъса за търсенето в индекса за множество пакети"
 
@@ -18149,6 +18286,14 @@ msgid "bad pack-int-id: %u (%u total packs)"
 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-битови отмествания, но размерът на "
@@ -18241,13 +18386,6 @@ msgstr "неправилна сума за проверка"
 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 "във файла с индекса за множество пакети няма идентификатори на обекти"
 
@@ -18792,6 +18930,11 @@ msgstr "задължителният обратен индекс липсва в
 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“ е неправилен"
@@ -18818,6 +18961,11 @@ msgstr ""
 "повредена битова маска във формат EWAH: отрязана заглавна част за битовата "
 "маска на подаване „%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“ липсва в битовата маска на видовете"
@@ -18917,6 +19065,9 @@ 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 "обратният индекс не може едновременно да се записва и да се проверява"
 
@@ -18987,10 +19138,6 @@ msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr ""
 "„%s“ очаква неотрицателно цяло число, евентуално със суфикс „k“/„m“/„g“"
 
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "опциите „%s“ и „%s“ са несъвместими"
-
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "нееднозначна опция: „%s“ (може да е „--%s%s“ или „--%s%s“)"
@@ -19315,10 +19462,6 @@ msgstr "файлът „%s“ не може да бъде индексиран"
 msgid "unable to add '%s' to index"
 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“ съществува и като файл, и като директория"
@@ -19864,7 +20007,7 @@ msgstr "неправилно име на клон: „%s = %s“"
 
 #, c-format
 msgid "ignoring dangling symref %s"
-msgstr "игнориране на указател на обект извън клон „%s“"
+msgstr "игноÑ\80иÑ\80ане Ð½Ð° Ñ\84айл Ñ\81 Ñ\83казаÑ\82ел Ð½Ð° Ð¾Ð±ÐµÐºÑ\82 Ð¸Ð·Ð²Ñ\8aн ÐºÐ»Ð¾Ð½ â\80\9e%sâ\80\9c"
 
 #, c-format
 msgid "log for ref %s has gap after %s"
@@ -19904,10 +20047,6 @@ msgstr "„%s“ съществува, не може да се създаде 
 msgid "cannot process '%s' and '%s' at the same time"
 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"
@@ -21333,6 +21472,9 @@ msgstr "Конфликти при прилагането на автоматич
 msgid "Autostash exists; creating a new stash entry."
 msgstr "Вече има запис за автоматично скатано, затова се създава нов запис."
 
+msgid "autostash reference is a symref"
+msgstr "указателят за автоматично скатано e файл с указател"
+
 msgid "could not detach HEAD"
 msgstr "указателят „HEAD“ не може да се отдели"
 
@@ -21656,6 +21798,10 @@ msgstr "шаблоните няма да бъдат копирани от „%s
 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 не се поддържат"
@@ -21666,17 +21812,18 @@ 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 "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 ""
@@ -21948,12 +22095,6 @@ msgstr ""
 "какъв брой записи в кеша на обектите-дървета да се отбележат като невалидни "
 "(стандартно е 0)"
 
-msgid "unhandled options"
-msgstr "неподдържани опции"
-
-msgid "error preparing revisions"
-msgstr "грешка при подготовката на версии"
-
 #, c-format
 msgid "commit %s is not marked reachable"
 msgstr "подаването „%s“ не е отбелязано като достижимо"
@@ -22110,9 +22251,6 @@ msgstr "протоколът не поддържа задаването на п
 msgid "invalid remote service path"
 msgstr "неправилен път на отдалечената услуга"
 
-msgid "operation not supported by protocol"
-msgstr "опцията не се поддържа от протокола"
-
 #, c-format
 msgid "can't connect to subservice %s"
 msgstr "неуспешно свързване към подуслугата „%s“"
@@ -22247,10 +22385,6 @@ msgstr "стойността на настройката „transport.color.*“
 msgid "support for protocol v2 not implemented yet"
 msgstr "протокол версия 2 все още не се поддържа"
 
-#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "непозната стойност за настройката „%s“: „%s“"
-
 #, c-format
 msgid "transport '%s' not allowed"
 msgstr "преносът по „%s“ не е позволен"
@@ -22304,6 +22438,9 @@ msgstr ""
 "спъсъкът с адреси на пратки обявени за налични от сървъра не може да се "
 "получи "
 
+msgid "operation not supported by protocol"
+msgstr "опцията не се поддържа от протокола"
+
 msgid "too-short tree object"
 msgstr "прекалено кратък обект-дърво"
 
@@ -23147,6 +23284,10 @@ msgstr "освен това в индекса има неподадени про
 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"
index d1a4e56c3a59f200b66d46a26bb93474297f7866..bcb4da80fb9afe7ad62f65326ff6e0564c236550 100644 (file)
--- a/po/ca.po
+++ b/po/ca.po
@@ -1,7 +1,7 @@
 # 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
 #
@@ -56,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»
 #
 # 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'infintiu
+#   - 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-11-13 18:55+0100\n"
-"PO-Revision-Date: 2023-11-13 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"
@@ -592,6 +593,7 @@ msgstr "«git apply --cached» ha fallat"
 #. 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 ""
@@ -1502,6 +1504,10 @@ msgstr "l'opció «%s» requereix «%s»"
 msgid "Unexpected option --output"
 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»"
@@ -1547,6 +1553,14 @@ 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"
 msgstr "Comentari amb cometes errònies en el fitxer «%s»: %s"
@@ -1643,6 +1657,7 @@ msgstr[1] "(aproximadament %d passos)"
 
 #. 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"
@@ -1728,17 +1743,20 @@ msgstr "no s'està seguint: informació ambigua per a la referència «%s»"
 #. 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"
@@ -2143,6 +2161,7 @@ msgstr "El cos de la comissió és:"
 #. 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 ""
@@ -2480,6 +2499,7 @@ msgstr "bisecant amb només una comissió %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 "N'esteu segur [Y/n]? "
 
@@ -2557,6 +2577,7 @@ msgstr "Cal començar per «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 "Voleu que ho faci per vostè [Y/n]? "
 
@@ -2777,6 +2798,7 @@ msgstr ""
 #. among various forms of relative timestamps, but
 #. your language may need more or fewer display
 #. columns.
+#.
 msgid "4 years, 11 months ago"
 msgstr "fa 4 anys i 11 mesos"
 
@@ -2838,12 +2860,12 @@ 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à completament fusionada.\n"
-"Si esteu segur que voleu suprimir-la, executeu «git branch -D %s»"
+msgid "the branch '%s' is not fully merged"
+msgstr "la branca «%s» no està completament fusionada"
+
+#, 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ó"
@@ -3884,8 +3906,8 @@ msgstr "agafa a la força (descarta qualsevol modificació local)"
 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)"
@@ -4137,9 +4159,6 @@ msgstr ""
 "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>]"
 
@@ -4228,6 +4247,9 @@ msgstr "directori de git"
 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"
 
@@ -4347,11 +4369,9 @@ msgstr "Hi ha massa arguments."
 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"
@@ -4481,14 +4501,14 @@ msgid ""
 "--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"
@@ -5007,6 +5027,7 @@ msgstr "reusa el missatge de la comissió especificada"
 
 #. TRANSLATORS: Leave "[(amend|reword):]" as-is,
 #. and only translate <commit>.
+#.
 msgid "[(amend|reword):]commit"
 msgstr "[(amend|reword):]commit"
 
@@ -5544,7 +5565,7 @@ msgstr "No s'ha trobat cap nom, no es pot descriure res."
 
 #, 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"
@@ -6820,6 +6841,7 @@ msgstr "s'ha especificat un nombre de fils no vàlid (%d) per a %s"
 #. TRANSLATORS: %s is the configuration
 #. variable for tweaking threads, currently
 #. grep.threads
+#.
 #, c-format
 msgid "no threads support, ignoring %s"
 msgstr "no s'admeten fils, s'ignorarà %s"
@@ -6828,6 +6850,10 @@ msgstr "no s'admeten fils, s'ignorarà %s"
 msgid "unable to read tree (%s)"
 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"
@@ -7246,10 +7272,6 @@ msgstr "hi ha una inconsistència seriosa d'inflació"
 msgid "SHA1 COLLISION FOUND WITH %s !"
 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"
@@ -7390,11 +7412,13 @@ msgstr "error fsck als objectes del paquet"
 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>]"
 
@@ -8110,6 +8134,12 @@ msgstr ""
 "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"
 
@@ -8131,6 +8161,12 @@ msgstr "en conflictes, usa la seva versió"
 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"
 
@@ -8221,9 +8257,6 @@ msgstr "--trivial-merge és incompatible amb totes les altres opcions"
 msgid "unknown strategy option: -X%s"
 msgstr "opció d'estratègia desconeguda: -X%s"
 
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base és incompatible amb --stdin"
-
 #, c-format
 msgid "malformed input line: '%s'."
 msgstr "línia d'entrada mal formada: «%s»."
@@ -8856,6 +8889,7 @@ msgstr "s'ha produït un error en copiar les notes de «%s» a «%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'està refusant %s les notes en %s (fora de refs/notes/)"
@@ -9165,6 +9199,10 @@ msgstr "S'estan comprimint els objectes"
 msgid "inconsistency with delta count"
 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-"
@@ -9430,10 +9468,10 @@ 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"
@@ -10397,13 +10435,6 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "«switch» «c» espera un valor numèric"
 
-msgid ""
-"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
-"autosquash"
-msgstr ""
-"les opcions «apply» són incompatibles amb rebase.autoSquash. Considereu "
-"afegir-hi --no-autosquash"
-
 msgid ""
 "apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
 "no-rebase-merges"
@@ -10922,6 +10953,7 @@ msgstr "(sense URL)"
 #. TRANSLATORS: the colon ':' should align
 #. with the one in " Fetch URL: %s"
 #. translation.
+#.
 #, c-format
 msgid "  Push  URL: %s"
 msgstr "  URL de pujada: %s"
@@ -11414,6 +11446,77 @@ msgstr "--convert-graft-file arguments"
 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 ""
@@ -11622,15 +11725,6 @@ msgstr "--prefix requereix un argument"
 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"
 
@@ -12040,10 +12134,6 @@ msgstr "no imprimeixis els resultats a stdout (útil amb --verify)"
 msgid "show refs from stdin that aren't in local repository"
 msgstr "mostra les referències de stdin que no siguin en el repositori local"
 
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "només es poden donar les opcions «%s», «%s», o «%s»"
-
 msgid ""
 "git sparse-checkout (init | list | set | add | reapply | disable | check-"
 "rules) [<options>]"
@@ -13519,28 +13609,28 @@ msgstr "No hi ha cap branca d'origen possible, inferint «--orphan»"
 
 #, c-format
 msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"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 orfe nova\n"
-"(branca sense comissions) per a aquest repositori, podeu fer-ho\n"
+"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 orphan branch\n"
+"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 orfe nova\n"
-"(branca sense comissions) per a aquest repositori, podeu fer-ho\n"
+"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"
@@ -13602,6 +13692,10 @@ msgstr "no s'ha pogut crear directori de «%s»"
 msgid "initializing"
 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»)"
@@ -13641,10 +13735,6 @@ 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"
 
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "les opcions «%s» i «%s» no es poden usar juntes"
-
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "agafa <branca> encara que sigui agafada en altre arbre de treball"
 
@@ -13654,8 +13744,8 @@ msgstr "crea una branca nova"
 msgid "create or reset a branch"
 msgstr "crea o restableix una branca"
 
-msgid "create unborn/orphaned branch"
-msgstr "crea una branca no nascuda/òrfena"
+msgid "create unborn branch"
+msgstr "crea una branca no nascuda"
 
 msgid "populate the new working tree"
 msgstr "emplena l'arbre de treball nou"
@@ -13679,11 +13769,8 @@ 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 "options '%s', and '%s' cannot be used together"
-msgstr "les opcions «%s» i «%s» no es poden usar juntes"
-
-msgid "<commit-ish>"
-msgstr "<commit-ish>"
+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"
@@ -14313,6 +14400,11 @@ msgstr "Empaqueta els objectes desempaquetats en un repositori"
 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"
 
@@ -14554,6 +14646,35 @@ msgstr "Una eina per a gestionar dipòsits Git grans"
 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 ""
@@ -14573,6 +14694,20 @@ msgid "commit-graph file is too small to hold %u chunks"
 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"
 
@@ -14586,6 +14721,9 @@ msgstr "la cadena del graf de comissions no coincideix"
 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 ""
@@ -14613,6 +14751,9 @@ 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"
 
@@ -15785,6 +15926,10 @@ msgid "Unknown value for 'diff.submodule' config variable: '%s'"
 msgstr ""
 "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"
@@ -15862,12 +16007,6 @@ msgstr "argument --color-moved incorrecte: %s"
 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"
@@ -15912,8 +16051,8 @@ msgstr "llegible per una màquina --stat"
 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"
@@ -15924,8 +16063,8 @@ msgstr ""
 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 ""
@@ -16108,12 +16247,6 @@ msgstr "genera diff usant l'algorisme «patience diff»"
 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>"
 
@@ -16499,12 +16632,14 @@ msgstr "s'ha produït un error en processar els acks: %d"
 
 #. TRANSLATORS: The parameter will be 'ready', a protocol
 #. keyword.
+#.
 #, c-format
 msgid "expected packfile to be sent after '%s'"
 msgstr "s'esperava que el fitxer de paquet s'enviés després de «%s»"
 
 #. TRANSLATORS: The parameter will be 'ready', a protocol
 #. keyword.
+#.
 #, c-format
 msgid "expected no other sections to be sent after no '%s'"
 msgstr "no s'esperava que cap altra secció s'enviés després de «%s»"
@@ -17322,6 +17457,7 @@ msgstr ""
 #. 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"
@@ -17356,6 +17492,7 @@ 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 ""
@@ -17616,6 +17753,12 @@ 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 «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"
 
@@ -17676,6 +17819,13 @@ msgstr ""
 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 "
@@ -17764,11 +17914,6 @@ msgstr "suma de verificació incorrecta"
 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"
 
@@ -17851,6 +17996,7 @@ msgstr "S'està refusant reescriure les notes en %s (fora de 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 "Valor erroni de %s: «%s»"
@@ -18069,6 +18215,7 @@ msgstr "no s'han pogut desempaquetar els continguts de %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 [objecte incorrecte]"
@@ -18077,6 +18224,7 @@ msgstr "%s [objecte incorrecte]"
 #. object output. E.g.:
 #. *
 #.    "deadbeef commit 2021-01-01 - Some Commit Message"
+#.
 #, c-format
 msgid "%s commit %s - %s"
 msgstr "%s comissió %s - %s"
@@ -18091,6 +18239,7 @@ msgstr "%s comissió %s - %s"
 #. *
 #. The third argument is the "tag" string
 #. from object.c.
+#.
 #, c-format
 msgid "%s tag %s - %s"
 msgstr "%s etiqueta %s - %s"
@@ -18100,18 +18249,21 @@ msgstr "%s etiqueta %s - %s"
 #. the tag itself. E.g.:
 #. *
 #.    "deadbeef [bad tag, could not parse it]"
+#.
 #, c-format
 msgid "%s [bad tag, could not parse it]"
 msgstr "%s [etiqueta malmesa, no s'ha pogut analitzar]"
 
 #. TRANSLATORS: This is a line of ambiguous <type>
 #. object output. E.g. "deadbeef tree".
+#.
 #, c-format
 msgid "%s tree"
 msgstr "arbre %s"
 
 #. TRANSLATORS: This is a line of ambiguous <type>
 #. object output. E.g. "deadbeef blob".
+#.
 #, c-format
 msgid "%s blob"
 msgstr "blob %s"
@@ -18123,6 +18275,7 @@ msgstr "l'id d'objecte curt %s és ambigu"
 #. 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"
@@ -18292,6 +18445,9 @@ msgstr "falta l'índex invers necessari al mapa de bits multipaquet"
 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"
@@ -18317,6 +18473,12 @@ msgstr ""
 "mapa de bits ewah malmès: capçalera truncada per al mapa de bits de la "
 "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"
@@ -18410,6 +18572,9 @@ 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"
 
@@ -18474,10 +18639,6 @@ msgstr "%s no és disponible"
 msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr "%s espera un valor enter no negatiu amb un sufix opcional k/m/g"
 
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s és incompatible amb %s"
-
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "opció ambigua: %s (pot ser --%s%s o --%s%s)"
@@ -18514,6 +18675,7 @@ msgstr "ús: %s"
 
 #. TRANSLATORS: the colon here should align with the
 #. one in "usage: %s" translation.
+#.
 #, c-format
 msgid "   or: %s"
 msgstr "   o: %s"
@@ -18536,6 +18698,7 @@ msgstr "   o: %s"
 #. 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"
@@ -18798,10 +18961,6 @@ msgstr "no es pot llegir indexar el fitxer «%s»"
 msgid "unable to add '%s' to index"
 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"
@@ -19383,10 +19542,6 @@ msgstr "«%s» existeix; no es pot crear «%s»"
 msgid "cannot process '%s' and '%s' at the same time"
 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"
@@ -19570,6 +19725,7 @@ msgstr ""
 #. 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"
@@ -19610,7 +19766,7 @@ msgid ""
 "'%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 ""
@@ -19619,7 +19775,7 @@ msgid ""
 "'%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 ""
@@ -20203,6 +20359,7 @@ msgstr "cometeu els vostres canvis o feu un «stash» per a procedir."
 
 #. TRANSLATORS: %s will be "revert", "cherry-pick" or
 #. "rebase".
+#.
 #, c-format
 msgid "%s: Unable to write new index file"
 msgstr "%s: No s'ha pogut escriure un fitxer d'índex nou"
@@ -20762,6 +20919,9 @@ msgid "Autostash exists; creating a new stash entry."
 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"
 
@@ -21005,7 +21165,8 @@ 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 ""
-"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)."
 
@@ -21077,6 +21238,10 @@ msgstr "no s'estan copiant plantilles de «%s»: %s"
 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"
@@ -21088,14 +21253,16 @@ 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 "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"
@@ -21365,12 +21532,6 @@ msgid "number of entries in the cache tree to invalidate (default 0)"
 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"
@@ -21523,9 +21684,6 @@ msgstr "el protocol no permet establir el camí del servei remot"
 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"
@@ -21657,10 +21815,6 @@ msgid "support for protocol v2 not implemented yet"
 msgstr ""
 "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»"
@@ -21714,6 +21868,9 @@ msgid "could not retrieve server-advertised bundle-uri list"
 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"
 
@@ -22555,6 +22712,10 @@ msgstr "addicionalment, el vostre índex conté canvis sense cometre."
 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"
@@ -22950,64 +23111,3 @@ msgstr "S'està ometent %s amb el sufix de còpia de seguretat «%s».\n"
 #, perl-format
 msgid "Do you really want to send %s? [y|N]: "
 msgstr "Esteu segur que voleu enviar %s? [y|N]: "
-
-#, 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"
-
-#, c-format
-#~ msgid "%s : incompatible with something else"
-#~ msgstr "%s: és incompatible amb alguna altra cosa"
-
-#~ msgid "Could not write patch"
-#~ msgstr "No s'ha pogut escriure el pedaç"
-
-#, c-format
-#~ msgid "Could not stat '%s'"
-#~ msgstr "No s'ha pogut fer stat a «%s»"
-
-#, c-format
-#~ msgid "Cannot delete branch '%s' checked out at '%s'"
-#~ msgstr "No es pot suprimir la branca «%s» agafada a «%s»"
-
-#~ msgid "unable to write new_index file"
-#~ msgstr "no s'ha pogut escriure el fitxer new_index"
-
-#~ msgid "do not apply config rules"
-#~ msgstr "no apliquis les regles de configuració"
-
-#~ msgid "join whitespace-continued values"
-#~ msgstr "uneix els valors continus amb espais en blanc"
-
-#~ msgid "set parsing options"
-#~ msgstr "estableix les opcions d'anàlisi"
-
-#~ msgid "cannot move directory over file"
-#~ msgstr "no es pot moure un directori sobre un fitxer"
-
-#~ msgid "cannot use --filter without --stdout"
-#~ msgstr "no es pot utilitzar --filter sense --stdout"
-
-#~ msgid "cannot use --max-pack-size with --cruft"
-#~ msgstr "no es pot usar --max-pack-size amb --cruft"
-
-#~ msgid "--strategy requires --merge or --interactive"
-#~ msgstr "--strategy requereix --merge o --interactive"
-
-#, 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"
-
-#~ msgid "--merge-base only works with commits"
-#~ msgstr "--merge-base només funciona amb comissions"
-
-#~ msgid "scalar clone [<options>] [--] <repo> [<dir>]"
-#~ msgstr "scalar clone [<opcions>] [--] <repositori> [<dir>]"
-
-#, c-format
-#~ msgid "could not rename '%s' to '%s'"
-#~ msgstr "no s'ha pogut canviar el nom «%s» a «%s»"
index 95a185afd2b211ecdeaf70132cadc7f999a8df68..37d6c8099836c5f335f8007d363e170af75ad540 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -8,8 +8,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-09 11:29+0100\n"
-"PO-Revision-Date: 2023-11-10 14:28+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"
@@ -1473,6 +1473,10 @@ msgstr "die Option '%s' erfordert '%s'"
 msgid "Unexpected option --output"
 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'"
@@ -1518,6 +1522,14 @@ 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"
@@ -1698,12 +1710,10 @@ msgstr ""
 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
@@ -2830,13 +2840,14 @@ 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"
 msgstr "Aktualisierung der Konfigurationsdatei fehlgeschlagen."
@@ -3905,8 +3916,8 @@ msgstr "Auschecken erzwingen (verwirft lokale Änderungen)"
 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)"
@@ -4160,9 +4171,6 @@ msgstr ""
 "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>]"
 
@@ -4256,6 +4264,9 @@ msgstr ".git-Verzeichnis"
 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"
 
@@ -4379,12 +4390,9 @@ msgstr "Zu viele Argumente."
 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"
@@ -4526,7 +4534,7 @@ msgid ""
 "--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 "
@@ -6892,7 +6900,6 @@ msgstr "grep: Fehler beim Erzeugen eines Thread: %s"
 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
@@ -6905,6 +6912,10 @@ msgstr "keine Unterstützung von Threads, '%s' wird ignoriert"
 msgid "unable to read tree (%s)"
 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"
@@ -7326,10 +7337,6 @@ msgstr "ernsthafte Inkonsistenz nach Dekomprimierung"
 msgid "SHA1 COLLISION FOUND WITH %s !"
 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."
@@ -7470,11 +7477,13 @@ msgstr "fsck Fehler beim Packen von Objekten"
 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>]"
 
@@ -8195,6 +8204,13 @@ msgstr ""
 "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"
 
@@ -8216,6 +8232,12 @@ msgstr "bei Konflikten ihre Variante verwenden"
 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"
 
@@ -8307,9 +8329,6 @@ msgstr "--trivial-merge ist mit allen anderen Optionen inkompatibel"
 msgid "unknown strategy option: -X%s"
 msgstr "unbekannte Strategie-Option: -X%s"
 
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base ist inkompatibel mit --stdin"
-
 #, c-format
 msgid "malformed input line: '%s'."
 msgstr "Fehlerhafte Eingabezeile: '%s'."
@@ -9264,6 +9283,10 @@ msgstr "Komprimiere Objekte"
 msgid "inconsistency with delta count"
 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-"
@@ -9533,10 +9556,10 @@ 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"
@@ -10547,13 +10570,6 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "Schalter `C' erwartet einen numerischen Wert."
 
-msgid ""
-"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
-"autosquash"
-msgstr ""
-"apply-Optionen sind mit rebase.autoSquash nicht kompatibel. Erwägen Sie das "
-"Hinzufügen von --no-autosquash"
-
 msgid ""
 "apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
 "no-rebase-merges"
@@ -11567,6 +11583,77 @@ msgstr "--convert-graft-file erwartet keine Argumente"
 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 ""
@@ -11778,15 +11865,6 @@ msgstr "--prefix benötigt ein Argument"
 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."
 
@@ -12202,10 +12280,6 @@ msgstr ""
 "Referenzen von der Standard-Eingabe anzeigen, die sich nicht im lokalen "
 "Repository befinden"
 
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "es kann nur eines von '%s', '%s' oder '%s' angegeben werden"
-
 msgid ""
 "git sparse-checkout (init | list | set | add | reapply | disable | check-"
 "rules) [<options>]"
@@ -13717,33 +13791,29 @@ 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 orphan branch\n"
+"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, um einen neuen verwaisten "
-"Branch\n"
-"(Branch ohne Commits) für dieses Repository zu erstellen, können Sie dies "
-"mit\n"
-"der Option --orphan tun:\n"
+"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 orphan branch\n"
+"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, um einen neuen verwaisten "
-"Branch\n"
-"(Branch ohne Commits) für dieses Repository zu erstellen, können Sie dies "
-"mit\n"
-"der Option --orphan tun:\n"
+"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"
 
@@ -13805,6 +13875,10 @@ msgstr "Konnte Verzeichnis '%s' nicht erstellen."
 msgid "initializing"
 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')"
@@ -13845,10 +13919,6 @@ msgstr ""
 "Referenz zu überschreiben\n"
 "oder rufen Sie diese zuerst ab"
 
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "'%s' und '%s' können nicht zusammen verwendet werden"
-
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr ""
 "<Branch> auschecken, auch wenn dieser bereits in einem anderen "
@@ -13860,8 +13930,8 @@ msgstr "neuen Branch erstellen"
 msgid "create or reset a branch"
 msgstr "Branch erstellen oder umsetzen"
 
-msgid "create unborn/orphaned branch"
-msgstr "ungeborenen/verwaisten Branch erstellen"
+msgid "create unborn branch"
+msgstr "ungeborenen Branch erzeugen"
 
 msgid "populate the new working tree"
 msgstr "das neue Arbeitsverzeichnis auschecken"
@@ -13886,11 +13956,8 @@ msgstr ""
 "die Optionen '%s', '%s' und '%s' können nicht gemeinsam verwendet werden"
 
 #, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "die Optionen '%s' und '%s' können nicht gemeinsam verwendet werden"
-
-msgid "<commit-ish>"
-msgstr "<Commit-Angabe>"
+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"
@@ -14541,6 +14608,11 @@ msgstr "ungepackte Objekte in einem Repository packen"
 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"
 
@@ -14785,6 +14857,32 @@ msgstr "Ein Werkzeug zur Verwaltung großer Git-Repositories"
 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"
@@ -14801,6 +14899,16 @@ msgstr "Hash-Version des Commit-Graph %X stimmt nicht mit Version %X überein"
 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"
 
@@ -14814,6 +14922,9 @@ msgstr "Commit-Graph Verkettung stimmt nicht überein"
 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"
@@ -14834,6 +14945,9 @@ 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"
 
@@ -16000,6 +16114,10 @@ msgstr ""
 msgid "Unknown value for 'diff.submodule' config variable: '%s'"
 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"
@@ -16082,13 +16200,6 @@ msgstr "ungültiges --color-moved Argument: %s"
 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"
@@ -16132,8 +16243,8 @@ msgstr "maschinenlesbare Ausgabe von --stat"
 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"
@@ -16144,8 +16255,8 @@ msgstr ""
 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 ""
@@ -16328,12 +16439,6 @@ msgstr "Änderungen durch Nutzung des Algorithmus \"Patience Diff\" erzeugen"
 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>"
 
@@ -17547,7 +17652,7 @@ msgstr ""
 #. 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 ""
@@ -17848,6 +17953,13 @@ msgstr "Lesen des Zwischenspeichers fehlgeschlagen"
 msgid "multi-pack-index OID fanout is of the wrong size"
 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"
 
@@ -17898,6 +18010,13 @@ msgstr "Falsche Reihenfolge bei Multi-Pack-Index Pack-Namen: '%s' vor '%s'"
 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"
@@ -17983,13 +18102,6 @@ msgstr "Prüfsumme nicht korrekt"
 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"
 
@@ -18304,7 +18416,7 @@ msgstr "%s [ungültiges Objekt]"
 #. 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"
@@ -18313,7 +18425,7 @@ msgstr "%s Commit %s - %s"
 #. 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.
@@ -18329,7 +18441,7 @@ msgstr "%s Tag %s - %s"
 #. 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]"
@@ -18525,6 +18637,9 @@ msgstr "Multi-Pack-Bitmap fehlt erforderlicher Reverse-Index"
 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"
@@ -18547,6 +18662,12 @@ msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
 msgstr ""
 "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"
@@ -18638,6 +18759,9 @@ 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"
@@ -18703,10 +18827,6 @@ msgstr ""
 "%s erwartet einen nicht-negativen Integer-Wert mit einem optionalen k/m/g "
 "Suffix"
 
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s ist inkompatibel mit %s."
-
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "Mehrdeutige Option: %s (kann --%s%s oder --%s%s sein)"
@@ -19030,10 +19150,6 @@ msgstr "Konnte Datei '%s' nicht indizieren."
 msgid "unable to add '%s' to index"
 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"
@@ -19612,10 +19728,6 @@ msgstr "'%s' existiert; kann '%s' nicht erstellen"
 msgid "cannot process '%s' and '%s' at the same time"
 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"
@@ -19810,16 +19922,13 @@ msgid ""
 "\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."
@@ -21004,6 +21113,9 @@ msgstr "Beim Anwenden des automatischen Stash traten Konflikte auf."
 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"
 
@@ -21323,6 +21435,10 @@ msgstr "kopiere keine Vorlagen von '%s': %s"
 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"
@@ -21334,14 +21450,16 @@ 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 "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"
@@ -21611,12 +21729,6 @@ msgstr ""
 "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."
@@ -21772,9 +21884,6 @@ msgstr ""
 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"
@@ -21904,10 +22013,6 @@ msgstr "Konnte transport.color.* Konfiguration nicht parsen."
 msgid "support for protocol v2 not implemented yet"
 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."
@@ -21961,6 +22066,9 @@ msgstr "bundle-uri Operation wird vom Protokoll nicht unterstützt"
 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"
 
@@ -22849,6 +22957,10 @@ msgid "cannot %s: Your index contains uncommitted changes."
 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"
index ee2e610ef1469c351f9d7825319bc68a71e73e9c..736a90f6bb68e1b25f3a666513e0b2de4930cb12 100644 (file)
--- a/po/fr.po
+++ b/po/fr.po
@@ -80,8 +80,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-08 04:57+0000\n"
-"PO-Revision-Date: 2023-11-11 10:00+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"
@@ -1525,6 +1525,10 @@ msgstr "l'option '%s' requiert '%s'"
 msgid "Unexpected option --output"
 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'"
@@ -1570,6 +1574,14 @@ 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"
@@ -2874,12 +2886,12 @@ 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"
+
+#, 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"
@@ -3937,8 +3949,8 @@ msgstr "forcer l'extraction (laisser tomber les modifications locales)"
 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)"
@@ -4192,9 +4204,6 @@ msgstr ""
 "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>]"
 
@@ -4285,6 +4294,9 @@ msgstr "gitdir"
 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"
 
@@ -4407,12 +4419,9 @@ msgstr "Trop d'arguments."
 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"
@@ -4546,14 +4555,14 @@ msgid ""
 "--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"
@@ -6906,6 +6915,10 @@ msgstr "pas de support des fils, ignore %s"
 msgid "unable to read tree (%s)"
 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"
@@ -7324,10 +7337,6 @@ msgstr "grave incohérence dans la décompression (inflate)"
 msgid "SHA1 COLLISION FOUND WITH %s !"
 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"
@@ -7469,12 +7478,14 @@ msgstr "erreur de fsck dans les objets paquets"
 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"
@@ -8204,6 +8215,13 @@ msgstr ""
 "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"
 
@@ -8225,6 +8243,12 @@ msgstr "pour les conflits, utiliser leur version (their)"
 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"
 
@@ -8315,9 +8339,6 @@ msgstr "--trivial-merge est incompatible avec d'autres options"
 msgid "unknown strategy option: -X%s"
 msgstr "option de stratégie inconnue : -X%s"
 
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base est incompatible avec --stdin"
-
 #, c-format
 msgid "malformed input line: '%s'."
 msgstr "ligne en entrée malformée : '%s'."
@@ -9264,6 +9285,10 @@ msgstr "Compression des objets"
 msgid "inconsistency with delta count"
 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-"
@@ -9527,10 +9552,10 @@ 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"
@@ -10519,13 +10544,6 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "l'option `C' attend un valeur numérique"
 
-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 ""
 "apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
 "no-rebase-merges"
@@ -11535,6 +11553,77 @@ msgstr "--convert-graft-file ne supporte aucun argument"
 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 ""
@@ -11746,15 +11835,6 @@ msgstr "--prefix exige un argument"
 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"
 
@@ -12171,10 +12251,6 @@ msgstr ""
 "afficher les références de l'entrée standard qui ne sont pas dans le dépôt "
 "local"
 
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "les options '%s', '%s' et '%s' sont mutuellement exclusives"
-
 msgid ""
 "git sparse-checkout (init | list | set | add | reapply | disable | check-"
 "rules) [<options>]"
@@ -13673,28 +13749,28 @@ msgstr "Aucune branche source possible, activation de '--orphan'"
 
 #, c-format
 msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"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"
-"orpheline (une branche sans commit) pour ce dépôt, vous pouvez le faire\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 orphan branch\n"
+"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"
-"orpheline (une branche sans commit) pour ce dépôt, vous pouvez le faire\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"
@@ -13756,6 +13832,10 @@ msgstr "impossible de créer le répertoire de '%s'"
 msgid "initializing"
 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')"
@@ -13797,10 +13877,6 @@ msgstr ""
 "on arrête ; utilisez 'add -f' pour passe outre ou récupérer le distant en "
 "premier"
 
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "'%s' et '%s' ne peuvent pas être utilisées ensemble"
-
 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 "
@@ -13812,8 +13888,8 @@ msgstr "créer une nouvelle branche"
 msgid "create or reset a branch"
 msgstr "créer ou réinitialiser une branche"
 
-msgid "create unborn/orphaned branch"
-msgstr "créer une branche non née/orpheline"
+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"
@@ -13835,11 +13911,9 @@ 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 "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>"
+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"
@@ -14480,6 +14554,11 @@ msgstr "Empaqueter les objets non-empaquetés d'un dépôt"
 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"
 
@@ -14722,6 +14801,35 @@ msgstr "Un outil pour gérer les grands dépôts Git"
 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 ""
@@ -14741,6 +14849,21 @@ msgstr ""
 msgid "commit-graph file is too small to hold %u chunks"
 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 tronçon de graphes de base"
 
@@ -14754,6 +14877,9 @@ msgstr "la chaîne de graphe de commit ne correspond pas"
 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 ""
@@ -14781,6 +14907,9 @@ 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"
 
@@ -15960,6 +16089,10 @@ msgid "Unknown value for 'diff.submodule' config variable: '%s'"
 msgstr ""
 "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"
@@ -16042,13 +16175,6 @@ msgstr "mauvais argument --color-moved : %s"
 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"
@@ -16092,8 +16218,8 @@ msgstr "--stat pour traitement automatique"
 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"
@@ -16104,8 +16230,8 @@ msgstr ""
 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 ""
@@ -16293,12 +16419,6 @@ msgid "generate diff using the \"histogram diff\" algorithm"
 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>"
 
@@ -17808,6 +17928,13 @@ msgstr "impossible de lire le cache"
 msgid "multi-pack-index OID fanout is of the wrong size"
 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"
@@ -17868,6 +17995,13 @@ msgstr ""
 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 stocke un décalage en 64-bit, mais off_t est trop petit"
@@ -17953,13 +18087,6 @@ msgstr "somme de contrôle incorrecte"
 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"
 
@@ -18486,6 +18613,9 @@ msgstr "l'index inverse requis manque dans l'index multi-paquet"
 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"
@@ -18507,6 +18637,10 @@ msgstr "table de recherche en bitmap corrompue : index de commit %u hors plage"
 msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
 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"
@@ -18599,6 +18733,9 @@ 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"
 
@@ -18662,10 +18799,6 @@ msgstr "%s n'est pas disponible"
 msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr "%s attend une valeur entière non négative avec une suffixe k/m/g"
 
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s est incompatible avec %s"
-
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "option ambigüe : %s (devrait être --%s%s ou --%s%s)"
@@ -18994,10 +19127,6 @@ msgstr "indexation du fichier '%s' impossible"
 msgid "unable to add '%s' to index"
 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"
@@ -19574,10 +19703,6 @@ msgstr "'%s' existe ; impossible de créer '%s'"
 msgid "cannot process '%s' and '%s' at the same time"
 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"
@@ -19773,7 +19898,7 @@ msgid ""
 "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"
@@ -20961,6 +21086,9 @@ msgid "Autostash exists; creating a new stash entry."
 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"
 
@@ -21279,6 +21407,10 @@ msgstr "pas de copie des modèles depuis '%s' : %s"
 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"
@@ -21290,14 +21422,16 @@ 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 "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"
@@ -21567,12 +21701,6 @@ msgstr "effacer l'arbre de cache avant chaque itération"
 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"
@@ -21732,9 +21860,6 @@ msgstr ""
 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"
@@ -21866,10 +21991,6 @@ msgstr "impossible d'analyser la configuration transport.color.*"
 msgid "support for protocol v2 not implemented yet"
 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"
@@ -21924,6 +22045,9 @@ msgid "could not retrieve server-advertised bundle-uri list"
 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"
 
@@ -22773,6 +22897,10 @@ msgstr "de plus, votre index contient des modifications non validées."
 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"
@@ -23174,6 +23302,61 @@ msgstr "%s sauté avec un suffix de sauvegarde '%s'.\n"
 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 ""
index 9698367606beb8480d967e616eb8ac960d773807..3e8b212b6178f52813fd16b603653a447692da7a 100644 (file)
--- a/po/id.po
+++ b/po/id.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-12 20:04+0700\n"
-"PO-Revision-Date: 2023-11-12 20:30+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"
@@ -909,7 +909,7 @@ msgid "unclosed quote"
 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"
 
@@ -924,12 +924,13 @@ 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-index.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/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
@@ -1752,6 +1753,11 @@ msgstr "opsi `%s' butuh '%s'"
 msgid "Unexpected option --output"
 msgstr "Opsi --output tak diharapkan"
 
+#: archive.c
+#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "parameter konfigurasi tambahan: '%s'"
+
 #: archive.c
 #, c-format
 msgid "Unknown archive format '%s'"
@@ -1808,6 +1814,17 @@ msgstr "mengabaikan blob gitattributes '%s' yang terlalu besar"
 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"
@@ -2359,8 +2376,8 @@ 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'"
@@ -2523,8 +2540,7 @@ msgstr "git write-tree gagal menulis sebuah pohon"
 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"
 
@@ -2710,8 +2726,9 @@ msgid "n"
 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"
 
@@ -3406,12 +3423,13 @@ 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
+#, 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"
@@ -4443,7 +4461,7 @@ msgstr "Tidak dapat melakukan reflog untuk '%s': %s\n"
 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"
 
@@ -4714,8 +4732,8 @@ msgid "new-branch"
 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)"
@@ -4783,7 +4801,7 @@ msgstr ""
 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"
@@ -5027,10 +5045,6 @@ msgstr ""
 "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>]"
@@ -5122,6 +5136,7 @@ msgid "create a shallow clone since a specific time"
 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"
 
@@ -5149,6 +5164,10 @@ msgstr "direktori git"
 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"
@@ -5299,13 +5318,10 @@ msgstr "Terlalu banyak argumen."
 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
@@ -5471,13 +5487,13 @@ msgid ""
 "--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
@@ -8400,6 +8416,11 @@ msgstr "tidak ada dukungan utas, abaikan %s"
 msgid "unable to read tree (%s)"
 msgstr "tidak dapat membaca pohon (%s)"
 
+#: 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"
@@ -8929,11 +8950,6 @@ msgstr "inkonsistensi inflate serius"
 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"
@@ -9111,11 +9127,13 @@ msgstr "kesalahan fsck dalam objek paket"
 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>]"
 
@@ -9280,7 +9298,7 @@ msgstr ""
 "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"
@@ -10036,6 +10054,14 @@ msgstr ""
 "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"
@@ -10064,6 +10090,14 @@ msgstr "untuk konflik, gunakan versi mereka"
 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"
@@ -10181,10 +10215,6 @@ msgstr "--trivial-merge tidak kompatibel dengan semua opsi lainnya"
 msgid "unknown strategy option: -X%s"
 msgstr "opsi strategi tidak dikenal: -X%s"
 
-#: builtin/merge-tree.c
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base tidak kompatibel dengan --stdin"
-
 #: builtin/merge-tree.c builtin/notes.c
 #, c-format
 msgid "malformed input line: '%s'."
@@ -10342,7 +10372,7 @@ msgstr "'%s' tidak menunjuk pada sebuah komit"
 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."
 
@@ -10350,7 +10380,7 @@ msgstr "Tidak dapat menulis indeks."
 msgid "Not handling anything other than two heads merge."
 msgstr "Tak tangani apapun selain penggabungan dua kepala."
 
-#: builtin/merge.c t/helper/test-fast-rebase.c
+#: builtin/merge.c
 #, c-format
 msgid "unable to write %s"
 msgstr "tidak dapat menulis %s"
@@ -11363,6 +11393,11 @@ msgstr "Memampatkan objek"
 msgid "inconsistency with delta count"
 msgstr "ketidakkonsistenan dengan hitungan delta"
 
+#: 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 ""
@@ -11694,10 +11729,10 @@ msgstr "Menghitung objek"
 #, 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 ""
@@ -12816,7 +12851,7 @@ msgid "The --edit-todo action can only be used during interactive rebase."
 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"
 
@@ -12862,14 +12897,6 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "tombol `C' harap nilai numerik"
 
-#: builtin/rebase.c
-msgid ""
-"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
-"autosquash"
-msgstr ""
-"opsi penerapan tidak kompatibel dengan rebase.autoSquash. "
-"Pertimbangkanmenambahkan --no-autosquash"
-
 #: builtin/rebase.c
 msgid ""
 "apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
@@ -13368,10 +13395,10 @@ msgid_plural ""
 "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
@@ -14108,6 +14135,94 @@ msgstr "--convert-graft-file tidak mengambil argumen"
 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]"
@@ -14370,18 +14485,6 @@ msgstr "--prefix butuh sebuah argumen"
 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"
@@ -14894,11 +14997,6 @@ msgstr ""
 "perlihatkan referensi dari masukan standar yang tidak ada dalam repositori "
 "lokal"
 
-#: builtin/show-ref.c
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "hanya salah satu dari '%s', '%s', dan '%s' dapat diberikan"
-
 #: builtin/sparse-checkout.c
 msgid ""
 "git sparse-checkout (init | list | set | add | reapply | disable | check-"
@@ -16720,7 +16818,7 @@ 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 orphan branch\n"
+"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"
@@ -16734,7 +16832,7 @@ msgstr ""
 #: builtin/worktree.c
 #, c-format
 msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"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"
@@ -16812,6 +16910,11 @@ msgstr "tidak dapat membuat direktori '%s'"
 msgid "initializing"
 msgstr "menginisialisasi"
 
+#: 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')"
@@ -16858,11 +16961,6 @@ msgstr ""
 "ada, berhenti; gunakan 'add -f' untuk menimpa atau mengambil remote\n"
 "terlebih dahulu"
 
-#: builtin/worktree.c
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "'%s' dan '%s' tidak dapat digunakan bersamaan"
-
 #: builtin/worktree.c
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr ""
@@ -16877,7 +16975,7 @@ msgid "create or reset a branch"
 msgstr "buat atau setel ulang sebuah cabang"
 
 #: builtin/worktree.c
-msgid "create unborn/orphaned branch"
+msgid "create unborn branch"
 msgstr "buat cabang belum lahir/yatim"
 
 #: builtin/worktree.c
@@ -16907,12 +17005,8 @@ msgstr "Opsi '%s', '%s', dan '%s' tidak dapat digunakan bersamaan"
 
 #: builtin/worktree.c
 #, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "Opsi '%s', dan '%s' tidak dapat digunakan bersamaan"
-
-#: builtin/worktree.c
-msgid "<commit-ish>"
-msgstr "<mirip-komit>"
+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"
@@ -17714,6 +17808,12 @@ msgstr "Pak objek tak terpak di dalam repositori"
 msgid "Create, list, delete refs to replace objects"
 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"
@@ -18031,6 +18131,39 @@ msgstr "Alat untuk mengelola repositori Git besar"
 msgid "commit-graph file is too small"
 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"
@@ -18051,6 +18184,19 @@ msgstr "versi hash grafik komit %X tidak cocok dengan versi %X"
 msgid "commit-graph file is too small to hold %u chunks"
 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"
@@ -18068,6 +18214,10 @@ msgstr "rantai grafik komit tidak cocok"
 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"
@@ -18094,6 +18244,10 @@ msgstr "grafik komit memerlukan pembuatan data meluap tapi tidak punya"
 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"
@@ -19505,6 +19659,11 @@ msgstr ""
 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 ""
@@ -19601,14 +19760,6 @@ msgstr "argumen --color-moved jelek: %s"
 msgid "invalid mode '%s' in --color-moved-ws"
 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"
@@ -19666,8 +19817,8 @@ msgid "output only the last line of --stat"
 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 ""
@@ -19680,8 +19831,8 @@ msgid "synonym for --dirstat=cumulative"
 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"
@@ -19913,14 +20064,6 @@ msgstr "buat diff menggunakan algoritma \"diff sabar\""
 msgid "generate diff using the \"histogram diff\" algorithm"
 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>"
@@ -21729,6 +21872,14 @@ msgstr "gagal membaca tembolok"
 msgid "multi-pack-index OID fanout is of the wrong size"
 msgstr "OID kipas-keluar indeks multipak salah ukuran"
 
+#: 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"
@@ -21790,6 +21941,15 @@ msgstr "nama pak indeks multipak tidak berurutan: '%s' sebelum '%s'"
 msgid "bad pack-int-id: %u (%u total packs)"
 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"
@@ -21897,14 +22057,6 @@ msgstr "checksum salah"
 msgid "Looking for referenced packfiles"
 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"
@@ -22542,6 +22694,10 @@ msgstr "bitmap multipak kehilangan indeks balik yang diperlukan"
 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"
@@ -22565,6 +22721,11 @@ msgstr "tabel pencarian bitmap rusak: indeks komit %u di luar jangkauan"
 msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
 msgstr "bitmap ewah rusak: kepala terpotong untuk bitmap komit \"%s\""
 
+#: 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"
@@ -22680,6 +22841,10 @@ msgstr "posisi indeks balik tidak valid pada %<PRIu64>: %<PRIu32> != %<PRIu32>"
 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"
@@ -22760,11 +22925,6 @@ msgstr ""
 "%s mengharapkan nilai bilangan bulat non negatif dengan akhiran opsional k/m/"
 "g"
 
-#: parse-options.c
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s tidak kompatibel dengan %s"
-
 #: parse-options.c
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
@@ -23164,11 +23324,6 @@ msgstr "tidak dapat menulis berkas indeks '%s'"
 msgid "unable to add '%s' to index"
 msgstr "tidak dapat menambahkan '%s' ke indeks"
 
-#: 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"
@@ -23855,17 +24010,12 @@ msgstr "'%s' ada; tidak dapat membuat '%s'"
 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"
@@ -24100,9 +24250,9 @@ msgid ""
 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"
@@ -25042,7 +25192,7 @@ msgstr "identitas pengarang tidak valid '%s'"
 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"
@@ -25536,6 +25686,10 @@ msgstr "Menerapkan stase otomatis menghasilkan konflik."
 msgid "Autostash exists; creating a new stash entry."
 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"
@@ -25916,6 +26070,11 @@ msgstr "tidak menyalin templat dari '%s': %s"
 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"
@@ -25931,14 +26090,16 @@ msgid "attempt to reinitialize repository with different hash"
 msgstr "mencoba menginisialisasi ulang repositori dengan hash yang berbeda"
 
 #: setup.c
-#, c-format
-msgid "%s already exists"
-msgstr "%s sudah ada"
+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 "re-init: ignored --initial-branch=%s"
-msgstr "re-init: --initial-branch=%s diabaikan"
+msgid "%s already exists"
+msgstr "%s sudah ada"
 
 #: setup.c
 #, c-format
@@ -26264,14 +26425,6 @@ msgstr "bersihkan pohon tembolok sebelum setiap iterasi"
 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"
@@ -26469,10 +26622,6 @@ msgstr "menyetel jalur layanan remote tidak didukung oleh protokol"
 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"
@@ -26636,11 +26785,6 @@ msgstr "tidak dapat menguraikan konfigurasi transport.color.*"
 msgid "support for protocol v2 not implemented yet"
 msgstr "dukungan untuk protokol v2 belum diterapkan"
 
-#: 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"
@@ -26701,6 +26845,10 @@ msgstr "operasi bundle-uri tidak didukung oleh protokol"
 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"
@@ -27722,6 +27870,11 @@ msgstr "juga indeks Anda berisi perubahan yang belum dikomit."
 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 "
index cd2a86a4a53eed36a7c4c079b18be9c1bfe13631..786c2f749e71b1973245e9d43e6f6ebaa619d637 100644 (file)
--- a/po/sv.po
+++ b/po/sv.po
@@ -1,14 +1,14 @@
 # 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.43.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-11-09 14:12+0100\n"
-"PO-Revision-Date: 2023-11-09 14:28+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: Svenska <tp-sv@listor.tp-sv.se>\n"
 "Language: sv\n"
@@ -1417,6 +1417,10 @@ 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”"
@@ -1462,6 +1466,14 @@ 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"
@@ -2582,7 +2594,7 @@ msgid "do not show object names of boundary commits (Default: off)"
 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"
@@ -2734,12 +2746,12 @@ 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"
+
+#, 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"
@@ -3772,8 +3784,8 @@ msgstr "tvinga utcheckning (kasta bort lokala ändringar)"
 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)"
@@ -4023,9 +4035,6 @@ msgstr ""
 "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>]"
 
@@ -4113,6 +4122,9 @@ msgstr "gitkat"
 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"
 
@@ -4228,12 +4240,9 @@ msgstr "För många argument."
 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"
@@ -4361,7 +4370,7 @@ msgid ""
 "--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 | "
@@ -6674,6 +6683,10 @@ msgstr "trådstöd saknas, ignorerar %s"
 msgid "unable to read tree (%s)"
 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"
@@ -7080,10 +7093,6 @@ msgstr "allvarlig inflate-inkonsekvens"
 msgid "SHA1 COLLISION FOUND WITH %s !"
 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"
@@ -7224,11 +7233,13 @@ msgstr "fsck-fel i packat objekt"
 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>]"
 
@@ -7927,6 +7938,12 @@ msgstr ""
 "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"
 
@@ -7948,6 +7965,12 @@ msgstr "för konflikter, använd deras version"
 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"
 
@@ -8038,9 +8061,6 @@ msgstr "--trivial-merge är inkompatibelt med andra flaggor"
 msgid "unknown strategy option: -X%s"
 msgstr "okänd strategiflagga: -X%s"
 
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base är inkompatibel med --stdin"
-
 #, c-format
 msgid "malformed input line: '%s'."
 msgstr "felaktig indatarad: ”%s”."
@@ -8966,6 +8986,10 @@ msgstr "Komprimerar objekt"
 msgid "inconsistency with delta count"
 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-"
@@ -9219,10 +9243,10 @@ 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"
@@ -10175,13 +10199,6 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "flaggan ”C” förväntar ett numeriskt värde"
 
-msgid ""
-"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
-"autosquash"
-msgstr ""
-"argument för ”apply” är inkompatibla med rebase.autoSquash. Överväg att "
-"lägga till --no-autosquash"
-
 msgid ""
 "apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
 "no-rebase-merges"
@@ -11167,6 +11184,76 @@ msgstr "--convert-graft-file tar inga argument"
 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 ""
@@ -11373,15 +11460,6 @@ msgstr "--prefix kräver ett argument"
 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"
 
@@ -11784,10 +11862,6 @@ msgstr "visa inte resultat på standard ut (användbart med --verify)"
 msgid "show refs from stdin that aren't in local repository"
 msgstr "visa referenser från standard in som inte finns i lokalt arkiv"
 
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "endast en av ”%s”, ”%s” och ”%s” kan anges"
-
 msgid ""
 "git sparse-checkout (init | list | set | add | reapply | disable | check-"
 "rules) [<options>]"
@@ -13232,13 +13306,13 @@ msgstr "Ingen möjlig källgren, använder ”--orphan”"
 
 #, c-format
 msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"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 föräldrals\n"
+"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"
@@ -13246,13 +13320,13 @@ msgstr ""
 
 #, c-format
 msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"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 föräldrals\n"
+"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"
@@ -13314,6 +13388,10 @@ 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”)"
@@ -13352,10 +13430,6 @@ msgstr ""
 "finns, avslutar; använd ”add -f” för att överstyra eller hämta från en fjärr "
 "först"
 
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "”%s” och ”%s” kan inte användas samtidigt"
-
 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"
@@ -13366,8 +13440,8 @@ msgstr "skapa en ny gren"
 msgid "create or reset a branch"
 msgstr "skapa eller återställ en gren"
 
-msgid "create unborn/orphaned branch"
-msgstr "skapa en ofödd/övergiven gren"
+msgid "create unborn branch"
+msgstr "skapa en ofödd gren"
 
 msgid "populate the new working tree"
 msgstr "befolka den nya arbetskatalogen"
@@ -13389,11 +13463,8 @@ msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "flaggorna ”%s”, ”%s” och ”%s” kan inte användas samtidigt"
 
 #, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "flaggorna ”%s” och ”%s” kan inte användas samtidigt"
-
-msgid "<commit-ish>"
-msgstr "<incheckning-igt>"
+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"
@@ -14006,6 +14077,11 @@ msgstr "Packa opackade objekt i ett arkiv"
 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"
 
@@ -14243,6 +14319,32 @@ msgstr "Verktyg för att hantera stora Git-arkiv"
 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"
@@ -14259,6 +14361,18 @@ msgstr "incheckningsgrafens hashversion %X stämmer inte med versionen %X"
 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"
 
@@ -14272,6 +14386,9 @@ msgstr "incheckningsgrafens kedja stämmer inte"
 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"
@@ -14292,6 +14409,9 @@ 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"
 
@@ -15430,6 +15550,10 @@ msgstr ""
 msgid "Unknown value for 'diff.submodule' config variable: '%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"
@@ -15509,12 +15633,6 @@ msgstr "felaktigt argument till --color-moved: %s"
 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”"
-
 #, c-format
 msgid "invalid argument to %s"
 msgstr "ogiltigt argument för %s"
@@ -15558,8 +15676,8 @@ msgstr "maskinläsbar --stat"
 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"
@@ -15569,8 +15687,8 @@ msgstr ""
 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"
@@ -15744,12 +15862,6 @@ 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"
-
 msgid "<text>"
 msgstr "<text>"
 
@@ -17220,6 +17332,12 @@ msgstr "misslyckades läsa cachen"
 msgid "multi-pack-index OID fanout is of the wrong size"
 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"
 
@@ -17270,6 +17388,13 @@ msgstr "paketnamn för multi-pack-index i fel ordning: ”%s” före ”%s”"
 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 innehåller 64-bitars offset, men off_t är för liten"
 
@@ -17353,12 +17478,6 @@ msgstr "felaktig kontrollsumma"
 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"
 
@@ -17875,6 +17994,9 @@ msgstr "flerpaketsbitkarta saknar nödvändigt omvänt index"
 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"
@@ -17894,6 +18016,10 @@ msgstr ""
 msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%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"
@@ -17985,6 +18111,9 @@ 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"
 
@@ -18047,10 +18176,6 @@ msgstr "%s är inte tillgängligt"
 msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr "%s förväntar ett icke-negativt heltalsvärde, med valfritt k/m/g-suffix"
 
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s är inkompatibel med %s"
-
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "tvetydig flagga: %s (kan vara --%s%s eller --%s%s)"
@@ -18364,10 +18489,6 @@ msgstr "kan inte indexera filen ”%s”"
 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”"
-
 #, c-format
 msgid "'%s' appears as both a file and as a directory"
 msgstr "”%s” finns både som en fil och en katalog"
@@ -18940,10 +19061,6 @@ msgstr "”%s” finns; kan inte skapa ”%s”"
 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"
-
 #, c-format
 msgid "could not delete reference %s: %s"
 msgstr "kunde inte ta bort referensen %s: %s"
@@ -19138,7 +19255,7 @@ msgstr ""
 "- 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"
+"  motsvarande ”refs/{heads,tags}/”-prefix på fjärrsidan.\n"
 "\n"
 "Inget av dem fungerade, så vi gav upp. Ange fullständig referens."
 
@@ -20299,6 +20416,9 @@ msgstr "Tillämpning av autostash gav konflikter."
 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"
 
@@ -20610,6 +20730,10 @@ msgstr "kopierade inte mallar från ”%s”: %s"
 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"
@@ -20621,14 +20745,14 @@ 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 "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"
@@ -20893,12 +21017,6 @@ msgstr "töm cacheträdet före varje iteration"
 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"
@@ -21054,9 +21172,6 @@ msgstr "protkollet stöder inte att sätta sökväg till fjärrtjänst"
 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"
@@ -21186,10 +21301,6 @@ msgstr "kunde inte tolka inställningen för transport.color.*"
 msgid "support for protocol v2 not implemented yet"
 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"
@@ -21243,6 +21354,9 @@ msgstr "bundle-uri-funktionen stöds inte av protokollet"
 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"
 
@@ -22066,6 +22180,10 @@ msgstr "dessutom innehåller dit index ändringar som inte har checkats in."
 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"
index f01962d1dbdb40028e6c949e6d86e6f9cd322d55..19d6661bbe6d237d608e997bd2c22fe84d1e8ad8 100644 (file)
--- 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                   #
 # 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                     #
@@ -85,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ı               #
@@ -94,8 +96,8 @@ msgid ""
 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-11-09 11:15+0300\n"
-"PO-Revision-Date: 2023-11 13:00+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"
@@ -1499,6 +1501,10 @@ msgstr "'%s' seçeneği '%s' gerektiriyor"
 msgid "Unexpected option --output"
 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'"
@@ -1544,6 +1550,14 @@ 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"
@@ -2823,12 +2837,12 @@ 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"
+
+#, 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"
@@ -3863,8 +3877,8 @@ msgstr "zorla çıkış yap (yerel değişiklikleri çöpe at)"
 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ı)"
@@ -4115,9 +4129,6 @@ msgstr ""
 "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>]"
 
@@ -4205,6 +4216,9 @@ msgstr "git dizini"
 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"
 
@@ -4322,11 +4336,9 @@ msgstr "Çok fazla argüman."
 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"
@@ -4454,11 +4466,11 @@ msgid ""
 "--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>"
@@ -6777,6 +6789,10 @@ msgstr "iş parçacığı desteği yok, %s yok sayılıyor"
 msgid "unable to read tree (%s)"
 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"
@@ -7189,10 +7205,6 @@ msgstr "ciddi şişirme programı tutarsızlığı"
 msgid "SHA1 COLLISION FOUND WITH %s !"
 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"
@@ -7333,13 +7345,15 @@ msgstr "paket nesnelerinde fsck hatası"
 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"
@@ -8039,6 +8053,13 @@ msgstr ""
 "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"
 
@@ -8060,6 +8081,12 @@ msgstr "çakışmalarda onların sürümünü kullan"
 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"
 
@@ -8150,9 +8177,6 @@ msgstr "--trivial-merge, tüm diğer seçeneklerle uyumsuz"
 msgid "unknown strategy option: -X%s"
 msgstr "bilinmeyen strateji seçeneği: -X%s"
 
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base, --stdin ile uyumsuz"
-
 #, c-format
 msgid "malformed input line: '%s'."
 msgstr "hatalı oluşturulmuş girdi satırı: '%s'."
@@ -9077,6 +9101,10 @@ msgstr "Nesneler sıkıştırılıyor"
 msgid "inconsistency with delta count"
 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-"
@@ -9332,10 +9360,10 @@ 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"
@@ -10298,13 +10326,6 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "'C' anahtarı sayısal bir değer bekliyor"
 
-msgid ""
-"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
-"autosquash"
-msgstr ""
-"seçenekleri uygula, rebase.autoSquash ile uyumlu değil. --no-autosquash "
-"eklemeyi düşünün"
-
 msgid ""
 "apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
 "no-rebase-merges"
@@ -10717,10 +10738,10 @@ msgid_plural ""
 "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
@@ -11298,6 +11319,76 @@ msgstr "--convert-graft-file argüman almaz"
 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 ""
@@ -11506,15 +11597,6 @@ msgstr "--prefix bir argüman gerektiriyor"
 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ı"
 
@@ -11920,9 +12002,6 @@ msgstr "sonuçları stdout'a yazdırma (--verify ile birlikte kullanışlı)"
 msgid "show refs from stdin that aren't in local repository"
 msgstr "stdin'den yerel bir depoda olmayan başvuruları göster"
 
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "yalnızca '%s', '%s' veya '%s' arasından biri verilebilir"
-
 msgid ""
 "git sparse-checkout (init | list | set | add | reapply | disable | check-"
 "rules) [<options>]"
@@ -13366,29 +13445,29 @@ msgstr "Olası kaynak dal yok, '--orphan' anlamı çıkarılıyor"
 
 #, c-format
 msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"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 yeni bir yetim dal içeren (işlemesiz dal) bir\n"
-"çalışma ağacı oluşturmak istediyseniz bunu --orphan bayrağı\n"
-"ile yapabilirsiniz:\n"
+"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 orphan branch\n"
+"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 yeni bir yetim dal içeren (işlemesiz dal) bir\n"
-"çalışma ağacı oluşturmak istediyseniz bunu --orphan bayrağı\n"
-"ile yapabilirsiniz:\n"
+"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"
 
@@ -13446,6 +13525,10 @@ msgstr "'%s' dizini oluşturulamadı"
 msgid "initializing"
 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')"
@@ -13483,10 +13566,6 @@ 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"
 
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "'%s' ve '%s' birlikte kullanılamaz"
-
 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"
 
@@ -13496,8 +13575,8 @@ msgstr "yeni bir dal oluştur"
 msgid "create or reset a branch"
 msgstr "yeni bir dal oluştur veya sıfırla"
 
-msgid "create unborn/orphaned branch"
-msgstr "doğmamış/yetim bırakılmış dal oluştur"
+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"
@@ -13519,11 +13598,8 @@ msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "'%s', '%s' ve '%s' seçenekleri birlikte kullanılamaz"
 
 #, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "'%s' ve '%s' seçenekleri birlikte kullanılamaz"
-
-msgid "<commit-ish>"
-msgstr "<işlememsi>"
+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"
@@ -14135,6 +14211,10 @@ msgstr "Bir depodaki paketlenmemiş nesneleri paketle"
 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"
 
@@ -14373,6 +14453,32 @@ msgstr "Büyük Git depolarını yönetmek için bir araç"
 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"
@@ -14389,6 +14495,17 @@ msgstr "commit-graph sağlama sürümü %X, %X ile eşleşmiyor"
 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"
 
@@ -14402,6 +14519,9 @@ msgstr "commit-graph zinciri eşleşmiyor"
 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"
@@ -14422,6 +14542,9 @@ 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"
 
@@ -15562,6 +15685,10 @@ msgstr ""
 msgid "Unknown value for 'diff.submodule' config variable: '%s'"
 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"
@@ -15642,13 +15769,6 @@ msgstr "hatalı --color-moved argümanı: %s"
 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"
@@ -15692,8 +15812,8 @@ msgstr "makinede okunabilen --stat"
 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"
@@ -15703,8 +15823,8 @@ msgstr ""
 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 ""
@@ -15879,12 +15999,6 @@ msgstr "diff'i \"patience diff\" algoritmasını kullanarak oluştur"
 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>"
 
@@ -17367,7 +17481,13 @@ msgid "failed to read the cache"
 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"
@@ -17415,6 +17535,13 @@ msgstr "multi-pack-index paket adlarının sırasız: '%s' şundan önce: '%s'"
 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"
 
@@ -17498,11 +17625,6 @@ msgstr "yanlış sağlama toplamı"
 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"
 
@@ -18022,9 +18144,12 @@ msgstr "çoklu paket biteşlemi gereken ters indeksi içermiyor"
 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"
@@ -18041,6 +18166,10 @@ msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
 msgstr ""
 "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ı"
@@ -18132,6 +18261,9 @@ 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"
 
@@ -18194,10 +18326,6 @@ msgstr "%s kullanılabilir değil"
 msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr "%s negatif olmayan bir tamsayı bekliyor, isteğe bağlı k/m/g eki ile"
 
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s, %s ile uyumsuz"
-
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "belirsiz seçenek: %s (--%s%s veya --%s%s olabilir)"
@@ -18511,10 +18639,6 @@ msgstr "'%s' dosyası indekslenemiyor"
 msgid "unable to add '%s' to index"
 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"
@@ -19086,10 +19210,6 @@ msgstr "'%s' mevcut; '%s' oluşturulamıyor"
 msgid "cannot process '%s' and '%s' at the same time"
 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"
@@ -20446,6 +20566,9 @@ msgstr "Kendiliğinden zulalama çakışmalara neden oldu."
 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ı"
 
@@ -20756,6 +20879,10 @@ msgstr "şablonlar '%s' konumundan kopyalanmıyor: %s"
 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"
@@ -20767,14 +20894,15 @@ 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 "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"
@@ -21039,12 +21167,6 @@ msgstr "her bir yinelemeden önce önbellek ağacını temizle"
 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"
@@ -21198,9 +21320,6 @@ msgstr "uzak servis yolu ayarlama protokol tarafından desteklenmiyor"
 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"
@@ -21330,10 +21449,6 @@ msgstr "transport.color.* yapılandırması ayrıştırılamadı"
 msgid "support for protocol v2 not implemented yet"
 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"
@@ -21387,6 +21502,9 @@ msgstr "bundle-uri işlemi protokol tarafından desteklenmiyor"
 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"
 
@@ -22210,6 +22328,10 @@ msgstr "Ek olarak, indeksiniz işlenmemiş değişiklikler içeriyor."
 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"
index 086890552a23c17f8721c9a364320be1a3ee3998..0507e387babbbd98578060270e3293ba84ccdda8 100644 (file)
--- a/po/uk.po
+++ b/po/uk.po
@@ -9,7 +9,7 @@ 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: 2023-11-09 14:34-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"
@@ -18,7 +18,7 @@ msgstr ""
 "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.1\n"
+"X-Generator: Poedit 3.4.2\n"
 
 #, c-format
 msgid "Huh (%s)?"
@@ -1443,6 +1443,10 @@ 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\""
@@ -1488,6 +1492,14 @@ 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"
@@ -1666,10 +1678,12 @@ msgstr ""
 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
@@ -2325,7 +2339,7 @@ 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}=<термін>]    "
+"git bisect start [--term-(new,bad)=<термін> --term-(old,good)=<термін>]    "
 "[--no-checkout] [--first-parent] [<поганий> [<добрий>...]] [--]    "
 "[<визначник шляху>...]"
 
@@ -2785,11 +2799,12 @@ 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\""
 
 msgid "update of config-file failed"
@@ -2856,11 +2871,11 @@ msgstr "неприпустима назва гілки: \"%s\""
 
 #, c-format
 msgid "no commit on branch '%s' yet"
-msgstr "поки що немає комітів в гілці \"%s\""
+msgstr "поки що немає комітів у гілці \"%s\""
 
 #, c-format
 msgid "no branch named '%s'"
-msgstr "немає гілки з ім’ям \"%s\""
+msgstr "немає гілки з назвою \"%s\""
 
 msgid "branch rename failed"
 msgstr "не вдалося перейменувати гілку"
@@ -3078,7 +3093,7 @@ msgid ""
 "'--set-upstream-to' instead"
 msgstr ""
 "опція \"--set-upstream\" більше не підтримується. Будь ласка, використовуйте "
-"\"--track\" або \"--set-upstream-to\""
+"\"--track\" або \"--set-upstream-to\" замість неї"
 
 msgid "git version:\n"
 msgstr "версія git:\n"
@@ -3848,8 +3863,8 @@ msgstr "переключити примусово (викинути локаль
 msgid "new-branch"
 msgstr "нова-гілка"
 
-msgid "new unparented branch"
-msgstr "нова Ð³Ñ\96лка Ð±ÐµÐ· Ð´Ð¶ÐµÑ\80ела"
+msgid "new unborn branch"
+msgstr "нова Ð½ÐµÐ½Ð°Ñ\80оджена Ð³Ñ\96лка"
 
 msgid "update ignored files (default)"
 msgstr "оновити ігноровані файли (за замовчуванням)"
@@ -4100,9 +4115,6 @@ msgstr ""
 "clean.requireForce встановлено у true за замовчуванням і не задано ні -i, ні "
 "-n, ні -f; відмовлено в прибиранні"
 
-msgid "-x and -X cannot be used together"
-msgstr "-x та -X не можна використовувати разом"
-
 msgid "git clone [<options>] [--] <repo> [<dir>]"
 msgstr "git clone [<опції>] [--] <сховище> [<директорія>]"
 
@@ -4193,6 +4205,9 @@ msgstr "git директорія"
 msgid "separate git dir from working tree"
 msgstr "відокремити git-директорію від робочого дерева"
 
+msgid "specify the reference format to use"
+msgstr "вкажіть формат посилання, який потрібно використовувати"
+
 msgid "key=value"
 msgstr "ключ=значення"
 
@@ -4311,11 +4326,9 @@ msgstr "Забагато аргументів."
 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"
@@ -4450,7 +4463,7 @@ msgid ""
 "--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] [--object-dir "
 "<директорія>] [--append] [--object-dir <директорія>] [--append\n"
@@ -4458,7 +4471,7 @@ msgstr ""
 "| --stdin-commits]\n"
 "                       [--changed-paths] [--[no-]max-new-filters <число>] [--"
 "[no-]progress]\n"
-"                       <опції розділення>"
+"                       <опції-розділення>"
 
 msgid "dir"
 msgstr "директорія"
@@ -6757,7 +6770,7 @@ msgid "scheduler to trigger git maintenance run"
 msgstr "планувальник для запуску обслуговування git"
 
 msgid "failed to set up maintenance schedule"
-msgstr "не вдалося встановити графік обслуговування"
+msgstr "не вдалося встановити розклад обслуговування"
 
 msgid "failed to add repo to global config"
 msgstr "не вдалося додати сховище до глобальної конфігурації"
@@ -6776,6 +6789,7 @@ msgstr "grep: не вдалося створити потік: %s"
 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
@@ -6788,6 +6802,10 @@ msgstr "немає підтримки потоків, ігнорування %s"
 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"
@@ -7200,10 +7218,6 @@ msgstr "серйозне неспівпадіння під час розпаку
 msgid "SHA1 COLLISION FOUND WITH %s !"
 msgstr "ВИЯВЛЕНО SHA1 КОЛІЗІЮ З %s!"
 
-#, c-format
-msgid "unable to read %s"
-msgstr "не вдалося прочитати %s"
-
 #, c-format
 msgid "cannot read existing object info %s"
 msgstr "неможливо прочитати інформацію про існуючий об’єкт %s"
@@ -7348,11 +7362,13 @@ 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[=<дозволи>]] [<директорія>]"
 
@@ -7400,8 +7416,8 @@ msgid ""
 "                       [--parse] [<file>...]"
 msgstr ""
 "git interpret-trailers [--in-place] [--trim-empty]\n"
-"                       [(--trailer (<ключ>|<аліасКлюча>)"
-"[(=|:)<значення>])...]\n"
+"                       [(--trailer <ключ>|"
+"<аліасКлюча>[(=|:)<значення>])...]\n"
 "                       [--parse] [<файл>...]"
 
 msgid "edit files in place"
@@ -7411,7 +7427,7 @@ msgid "trim empty trailers"
 msgstr "обрізати порожні причепи"
 
 msgid "placement"
-msgstr "розташування"
+msgstr "розміщення"
 
 msgid "where to place the new trailer"
 msgstr "де розмістити новий причіп"
@@ -8065,11 +8081,18 @@ 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 "використовувати ID обʼєктів замість назв файлів"
+msgstr "використовувати ідентифікатори обʼєктів замість назв файлів"
 
 msgid "use a diff3 based merge"
 msgstr "використовувати злиття на основі diff3"
@@ -8086,6 +8109,12 @@ 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 "у разі конфліктів використовувати цей розмір маркера"
 
@@ -8097,7 +8126,7 @@ msgstr "встановити мітки для файл1/оріг-файл/фа
 
 #, c-format
 msgid "object '%s' does not exist"
-msgstr "обʼєкт \"%s\" не існує"
+msgstr "обєкт \"%s\" не існує"
 
 msgid "Could not write object file"
 msgstr "Не вдалося записати файл обʼєкта"
@@ -8177,9 +8206,6 @@ msgstr "--trivial-merge несумісна з усіма іншими опція
 msgid "unknown strategy option: -X%s"
 msgstr "невідомий варіант стратегії: -X%s"
 
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base несумісна з --stdin"
-
 #, c-format
 msgid "malformed input line: '%s'."
 msgstr "невірно сформований рядок вводу: \"%s\"."
@@ -9112,6 +9138,10 @@ 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-"
@@ -9368,10 +9398,10 @@ 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"
@@ -10360,13 +10390,6 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "перемикач \"C\" очікує числове значення"
 
-msgid ""
-"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
-"autosquash"
-msgstr ""
-"apply опції несумісні з rebase.autoSquash.  Розгляньте можливість додавання "
-"--no-autosquash"
-
 msgid ""
 "apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
 "no-rebase-merges"
@@ -11156,7 +11179,7 @@ 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 "пÑ\80еÑ\84Ñ\96кÑ\81 Ð¿Ð°ÐºÑ\83нка Ð´Ð»Ñ\8f Ð·Ð±ÐµÑ\80Ñ\96ганнÑ\8f Ð¿Ð°ÐºÑ\83нка Ð· Ð¾Ð±Ñ\80Ñ\96заними Ð¾Ð±Ê¼Ñ\94кÑ\82ами"
+msgstr "префікс для зберігання пакунка з обрізаними обʼєктами"
 
 msgid "pack prefix to store a pack containing filtered out objects"
 msgstr "префікс для зберігання пакунка з відфільтрованими  обʼєктами"
@@ -11166,7 +11189,7 @@ msgstr "неможливо видалити пакунки в precious-objects 
 
 #, c-format
 msgid "option '%s' can only be used along with '%s'"
-msgstr "опÑ\86Ñ\96Ñ\8e \"%s\" Ð¼Ð¾Ð¶Ð½Ð° Ð²Ð¸ÐºÐ¾Ñ\80иÑ\81Ñ\82овÑ\83ваÑ\82и тільки разом з \"%s\""
+msgstr "опÑ\86Ñ\96Ñ\8f \"%s\" Ð¼Ð¾Ð¶Ðµ Ð±Ñ\83Ñ\82и Ð²Ð¸ÐºÐ¾Ñ\80иÑ\81Ñ\82ана тільки разом з \"%s\""
 
 msgid "Nothing new to pack."
 msgstr "Немає нічого нового для пакування."
@@ -11371,6 +11394,75 @@ 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 ""
@@ -11583,15 +11675,6 @@ msgstr "--prefix потребує аргументу"
 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 "цю операцію треба виконувати в робочому дереві"
 
@@ -12005,10 +12088,6 @@ msgstr "не виводити результати у stdout (корисно з
 msgid "show refs from stdin that aren't in local repository"
 msgstr "показати посилання з stdin, яких немає в локальному сховищі"
 
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "можна вказати тільки один з \"%s\", \"%s\" або \"%s\""
-
 msgid ""
 "git sparse-checkout (init | list | set | add | reapply | disable | check-"
 "rules) [<options>]"
@@ -12820,7 +12899,7 @@ msgstr ""
 "branch] [--] [<шлях>...]"
 
 msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Ð\9dе Ð²Ð´Ð°Ð»Ð¾Ñ\81Ñ\8f Ñ\80озпÑ\96знаÑ\82и HEAD Ñ\8fк дійсне посилання."
+msgstr "Ð\9dе Ð²Ð´Ð°Ð»Ð¾Ñ\81Ñ\8f Ñ\80озвʼÑ\8fзаÑ\82и HEAD Ð² дійсне посилання."
 
 msgid "git submodule absorbgitdirs [<options>] [<path>...]"
 msgstr "git submodule absorbgitdirs [<опції>] [<шлях>...]"
@@ -13325,7 +13404,7 @@ msgstr "%d\n"
 
 #, c-format
 msgid "index-version: was %d, set to %d"
-msgstr "index-version: було %d, стало %d"
+msgstr "версія індексу: була %d, стала %d"
 
 msgid ""
 "core.splitIndex is set to false; remove or change it, if you really want to "
@@ -13482,13 +13561,13 @@ msgstr "Немає можливої джерельної гілки, що озн
 
 #, c-format
 msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"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"
 "(гілку без комітів) для цього сховища, ви можете зробити це\n"
 "за допомогою прапорця --orphan:\n"
 "\n"
@@ -13496,13 +13575,13 @@ msgstr ""
 
 #, c-format
 msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"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"
 "(гілку без комітів) для цього сховища, ви можете зробити це\n"
 "за допомогою прапорця --orphan:\n"
 "\n"
@@ -13565,6 +13644,10 @@ 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\")"
@@ -13601,12 +13684,8 @@ msgid ""
 msgstr ""
 "Не існує локальних або віддалених посилань, незважаючи на наявність "
 "принаймні одного віддаленого\n"
-"призначення, зупинка; скористайтесь \"add -f\" для перевизначення або "
-"спочатку виконайте отримання віддаленого посилання"
-
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "\"%s\" і \"%s\" не можна використовувати разом"
+"призначення, зупинка; скористайтесь \"add -f\", щоб перевизначити, або "
+"спочатку виконайте отримання з віддаленного сховища"
 
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr ""
@@ -13618,8 +13697,8 @@ msgstr "створити нову гілку"
 msgid "create or reset a branch"
 msgstr "створити або скинути гілку"
 
-msgid "create unborn/orphaned branch"
-msgstr "створити ненароджену/сирітську гілку"
+msgid "create unborn branch"
+msgstr "створити ненароджену гілку"
 
 msgid "populate the new working tree"
 msgstr "заповнити нове робоче дерево"
@@ -13642,11 +13721,8 @@ msgid "options '%s', '%s', and '%s' cannot be used together"
 msgstr "опції \"%s\", \"%s\" та \"%s\" не можна використовувати разом"
 
 #, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "опції \"%s\" і \"%s\" не можна використовувати разом"
-
-msgid "<commit-ish>"
-msgstr "<комітоподібне>"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "опцію \"%s\" не можна використовувати разом з комітоподібними"
 
 msgid "added with --lock"
 msgstr "додано з --lock"
@@ -13926,7 +14002,7 @@ msgstr "ідентифікатор завершення фрагмента зʼ
 
 #, c-format
 msgid "chunk id %<PRIx32> not %d-byte aligned"
-msgstr "шматок в id %<PRIx32> не %d-byte впорядкований"
+msgstr "шматок id %<PRIx32> не є %d-byte впорядкованим"
 
 #, c-format
 msgid "improper chunk offset(s) %<PRIx64> and %<PRIx64>"
@@ -13981,7 +14057,7 @@ msgid "Move objects and refs by archive"
 msgstr "Перенести архів обʼєктів та посилань"
 
 msgid "Provide contents or details of repository objects"
-msgstr "Ð\9fоказаÑ\82и Ð²Ð¼Ñ\96Ñ\81Ñ\82 Ð°Ð±Ð¾ Ñ\96нÑ\84оÑ\80маÑ\86Ñ\96Ñ\8e Ð¿Ñ\80о Ð¾Ð±Ê¼Ñ\94кÑ\82и сховища"
+msgstr "Ð\9dадаваÑ\82и Ð²Ð¼Ñ\96Ñ\81Ñ\82 Ð°Ð±Ð¾ Ð´ÐµÑ\82алÑ\96 Ð¾Ð±Ê¼Ñ\94кÑ\82Ñ\96в сховища"
 
 msgid "Display gitattributes information"
 msgstr "Відобразити інформацію про gitattributes"
@@ -14269,6 +14345,11 @@ 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 "Створює підсумок змін для розгляду"
 
@@ -14391,7 +14472,7 @@ msgid "Display version information about Git"
 msgstr "Показати інформацію про версію Git"
 
 msgid "Show logs with differences each commit introduces"
-msgstr "Показати журнал з різницею, яку вносить кожен з комітів"
+msgstr "Показати журнал з різницями, які вносить кожен коміт"
 
 msgid "Manage multiple working trees"
 msgstr "Керувати кількома робочими деревами"
@@ -14507,6 +14588,32 @@ msgstr "Інструмент для керування великими схов
 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"
@@ -14523,11 +14630,20 @@ msgstr "хеш версія коміт-графа %X не збігається 
 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 "занадто малий шматок базових графів коміт-графа"
+msgstr "шматок base graphs коміт-графа занадто малий"
 
 msgid "commit-graph chain does not match"
 msgstr "ланцюжок коміт-графа не співпадає"
@@ -14536,6 +14652,9 @@ msgstr "ланцюжок коміт-графа не співпадає"
 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\" не є хешем"
@@ -14551,10 +14670,13 @@ msgid "could not find commit %s"
 msgstr "не вдалося знайти коміт %s"
 
 msgid "commit-graph requires overflow generation data but has none"
-msgstr "коміт-граф потребує даних генерації переповнення, але їх немаєданих"
+msgstr "коміт-граф потребує даних генерації переповнення, але не має їх"
 
 msgid "commit-graph overflow generation data is too small"
-msgstr "занадто мало даних про переповнення коміт-граф генерації"
+msgstr "занадто мало даних генерації переповнення коміт-графа"
+
+msgid "commit-graph extra-edges pointer out of bounds"
+msgstr "extra-edges pointer коміт-графа виходить за межі"
 
 msgid "Loading known commits in commit graph"
 msgstr "Завантаження відомих комітів у коміт-графі"
@@ -14692,7 +14814,8 @@ msgid ""
 "commit-graph has both zero and non-zero generations (e.g., commits '%s' and "
 "'%s')"
 msgstr ""
-"коміт-граф має як нульові, так і ненульові генерації (коміти \"%s\" і \"%s\")"
+"коміт-граф має як нульові, так і ненульові генерації (наприклад, коміти "
+"\"%s\" і \"%s\")"
 
 msgid "Verifying commits in commit graph"
 msgstr "Перевірка комітів у коміт-графі"
@@ -15702,6 +15825,10 @@ msgstr ""
 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"
@@ -15782,13 +15909,6 @@ msgstr "невірний --color-moved аргумент: %s"
 msgid "invalid mode '%s' in --color-moved-ws"
 msgstr "неприпустимий режим \"%s\" у --color-moved-ws"
 
-msgid ""
-"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
-"\"histogram\""
-msgstr ""
-"опція diff-algorithm приймає значення \"myers\", \"minimal\", \"patience\" "
-"та \"histogram\""
-
 #, c-format
 msgid "invalid argument to %s"
 msgstr "неприпустимий аргумент до %s"
@@ -15832,8 +15952,8 @@ msgstr "машинний вивід --stat"
 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"
@@ -15842,8 +15962,8 @@ msgstr "вивести розподіл відносної кількості з
 msgid "synonym for --dirstat=cumulative"
 msgstr "синонім для --dirstat=cumulative"
 
-msgid "synonym for --dirstat=files,param1,param2..."
-msgstr "синонім для --dirstat=files,параметр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 ""
@@ -16019,12 +16139,6 @@ msgstr "згенерувати різницю за алгоритмом \"patien
 msgid "generate diff using the \"histogram diff\" algorithm"
 msgstr "згенерувати різницю за алгоритмом \"histogram diff\""
 
-msgid "<algorithm>"
-msgstr "<алгоритм>"
-
-msgid "choose a diff algorithm"
-msgstr "вибрати алгоритм різниці"
-
 msgid "<text>"
 msgstr "<текст>"
 
@@ -17224,7 +17338,7 @@ msgstr ""
 #. 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 ""
@@ -17513,12 +17627,18 @@ 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 має невірний розмір"
+msgstr "multi-pack-index OID lookup шматок має невірний розмір"
 
 msgid "multi-pack-index object offset chunk is the wrong size"
-msgstr ""
-"multi-pack-index необхідній шматок зміщення обʼєкта має невірний розмір"
+msgstr "multi-pack-index object offset шматок має невірний розмір"
 
 #, c-format
 msgid "multi-pack-index file %s is too small"
@@ -17537,24 +17657,22 @@ 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 ""
-"multi-pack-index необхідний шматок імені пакунка відсутній або пошкоджений"
+msgstr "необхідний шматок pack-name multi-pack-index відсутній або пошкоджений"
 
 msgid "multi-pack-index required OID fanout chunk missing or corrupted"
 msgstr ""
-"multi-pack-index необхідний шматок розсіювання OID відсутній або пошкоджений"
+"необхідний шматок OID fanout multi-pack-index відсутній або пошкоджений"
 
 msgid "multi-pack-index required OID lookup chunk missing or corrupted"
 msgstr ""
-"multi-pack-index необхідний шматок пошуку OID відсутній або пошкоджений"
+"необхідний шматок OID lookup multi-pack-index відсутній або пошкоджений"
 
 msgid "multi-pack-index required object offsets chunk missing or corrupted"
 msgstr ""
-"multi-pack-index необхідний шматок зміщення обʼєктів відсутній або "
-"пошкоджений"
+"необхідний шматок object offsets multi-pack-index відсутній або пошкоджений"
 
 msgid "multi-pack-index pack-name chunk is too short"
-msgstr "multi-pack-index  pack-name шматок занадто малий"
+msgstr "шматок pack-name multi-pack-index занадто малий"
 
 #, c-format
 msgid "multi-pack-index pack names out of order: '%s' before '%s'"
@@ -17566,12 +17684,19 @@ msgstr ""
 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 "multi-pack-index large зсув виходить за межі"
+msgstr "large offset multi-pack-index виходить за межі"
 
 #, c-format
 msgid "failed to add packfile '%s'"
@@ -17651,13 +17776,6 @@ msgstr "невірна контрольна сума"
 msgid "Looking for referenced packfiles"
 msgstr "Пошук файлів пакунків, на які є посилання"
 
-#, c-format
-msgid ""
-"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
-msgstr ""
-"невірна послідовність oid розсіювання: fanout[%d] = %<PRIx32> > %<PRIx32> = "
-"fanout[%d]"
-
 msgid "the midx contains no oid"
 msgstr "midx не містить oid"
 
@@ -17961,7 +18079,7 @@ msgstr "%s [невірний обʼект]"
 #. 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"
@@ -17970,7 +18088,7 @@ msgstr "%s коміт %s - %s"
 #. 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.
@@ -17986,7 +18104,7 @@ msgstr "%s тег %s - %s"
 #. 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]"
@@ -18182,6 +18300,9 @@ msgstr "у мультіпакунковому bitmap відсутній необ
 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) є неприпустимим"
@@ -18202,6 +18323,12 @@ msgstr ""
 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"
@@ -18296,6 +18423,9 @@ msgstr ""
 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 "неможливо одночасно записувати та звіряти зворотний індекс"
 
@@ -18358,10 +18488,6 @@ msgstr "%s недоступний"
 msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr "%s очікує невід'ємне ціле значення з опціональним суфіксом k/m/g"
 
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s несумісний з %s"
-
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
 msgstr "неоднозначна опція: %s (може бути --%s%s або --%s%s)"
@@ -18469,7 +18595,7 @@ msgstr ""
 
 #, c-format
 msgid "bad boolean environment value '%s' for '%s'"
-msgstr "невÑ\96Ñ\80не Ð±Ñ\83леве Ð·Ð½Ð°Ñ\87еннÑ\8f ÐºÐ¾Ð½Ñ\84Ñ\96гÑ\83Ñ\80аÑ\86Ñ\96Ñ\97 \"%s\" для \"%s\""
+msgstr "невÑ\96Ñ\80не Ð±Ñ\83леве Ð·Ð½Ð°Ñ\87еннÑ\8f Ð¾Ñ\82оÑ\87еннÑ\8f \"%s\" для \"%s\""
 
 #, c-format
 msgid "failed to parse %s"
@@ -18683,10 +18809,6 @@ msgstr "не вдалося проіндексувати файл \"%s\""
 msgid "unable to add '%s' to index"
 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\" відображається як файл і як каталог"
@@ -19260,10 +19382,6 @@ msgstr "\"%s\" існує; неможливо створити \"%s\""
 msgid "cannot process '%s' and '%s' at the same time"
 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"
@@ -20651,6 +20769,9 @@ msgstr "Застосування автосхову призвело до кон
 msgid "Autostash exists; creating a new stash entry."
 msgstr "Автосхов існує; створення нового запису схову."
 
+msgid "autostash reference is a symref"
+msgstr "посилання автосхову є символьним посиланням"
+
 msgid "could not detach HEAD"
 msgstr "не вдалося відʼєднати HEAD"
 
@@ -20971,6 +21092,10 @@ msgstr "не копіюються шаблони з \"%s\": %s"
 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"
@@ -20982,14 +21107,14 @@ 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 "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"
@@ -21261,12 +21386,6 @@ msgid "number of entries in the cache tree to invalidate (default 0)"
 msgstr ""
 "кількість записів у дереві кешу, які потрібно анулювати (за замовчуванням 0)"
 
-msgid "unhandled options"
-msgstr "необроблені опції"
-
-msgid "error preparing revisions"
-msgstr "помилка при підготовці ревізій"
-
 #, c-format
 msgid "commit %s is not marked reachable"
 msgstr "коміт %s не позначений як досяжний"
@@ -21423,9 +21542,6 @@ msgstr "встановлення шляху до віддаленого серв
 msgid "invalid remote service path"
 msgstr "неприпустимий шлях до віддаленої служби"
 
-msgid "operation not supported by protocol"
-msgstr "операція не підтримується протоколом"
-
 #, c-format
 msgid "can't connect to subservice %s"
 msgstr "неможливо підключитися до підсервісу %s"
@@ -21557,10 +21673,6 @@ msgstr "не вдалося розібрати конфігурацію transpor
 msgid "support for protocol v2 not implemented yet"
 msgstr "підтримка протоколу v2 ще не запроваджена"
 
-#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "невідоме значення для конфігурації \"%s\": %s"
-
 #, c-format
 msgid "transport '%s' not allowed"
 msgstr "засіб передачі \"%s\" не дозволений"
@@ -21613,6 +21725,9 @@ 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 "занадто короткий обʼєкт дерева"
 
@@ -22014,7 +22129,7 @@ msgid "unable to get random bytes"
 msgstr "не вдалося отримати випадкові байти"
 
 msgid "Unmerged paths:"
-msgstr "не злиті шляхи:"
+msgstr "Ð\9dе злиті шляхи:"
 
 msgid "  (use \"git restore --staged <file>...\" to unstage)"
 msgstr ""
@@ -22466,6 +22581,10 @@ msgstr "крім того, ваш індекс містить незакоміч
 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"
index 86402725b28399d5abc0e083bf17ba9b4d78e9de..39efaf1012a436a567112c7ea346d30456700e0b 100644 (file)
@@ -46,6 +46,7 @@
 #   commit                           |  提交
 #   commit message                   |  提交说明
 #   commit object                    |  提交对象
+#   commit-graph                     |  提交图
 #   commit-ish (also committish)     |  提交号
 #   cone                             |  锥形(稀疏检出模型);锥(稀疏检出)
 #   conflict                         |  冲突
 #   plumbing                         |  管件(Git 底层核心命令的别称)
 #   porcelain                        |  瓷件(Git 上层封装命令的别称)
 #   precious-objects repo            |  珍品仓库
+#   preferred pack                   |  首选包(多包索引中引入的首选包概念)
 #   promisor                         |  承诺者
 #   prune                            |  清除
 #   pull                             |  拉,拉取
@@ -151,8 +153,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2023-11-10 10:49+0800\n"
-"PO-Revision-Date: 2023-11-10 17:13+0800\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"
@@ -1031,7 +1033,7 @@ msgid "unclosed quote"
 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 "太多参数"
 
@@ -1046,12 +1048,13 @@ msgid "unrecognized whitespace ignore option '%s'"
 msgstr "未能识别的空白字符忽略选项 '%s'"
 
 #: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout-index.c
-#: builtin/checkout.c builtin/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/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
@@ -1859,6 +1862,11 @@ msgstr "选项 '%s' 需要 '%s'"
 msgid "Unexpected option --output"
 msgstr "未知参数 --output"
 
+#: archive.c
+#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "额外的命令行参数:'%s'"
+
 #: archive.c
 #, c-format
 msgid "Unknown archive format '%s'"
@@ -1915,6 +1923,17 @@ msgstr "忽略过大的 gitattributes 数据对象 '%s'"
 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"
@@ -2448,8 +2467,8 @@ 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'"
@@ -2602,8 +2621,7 @@ msgstr "git write-tree 无法写入树对象"
 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 "无法写提交对象"
 
@@ -2783,8 +2801,9 @@ msgid "n"
 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 "格式"
 
@@ -2916,7 +2935,9 @@ 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] [<坏> [<好>...]] [--]    [<路径规格>...]"
+msgstr ""
+"git bisect start [--term-{new|bad}=<术语> --term-{old|good}=<术语>]    [--no-"
+"checkout] [--first-parent] [<坏> [<好>...]] [--]    [<路径规格>...]"
 
 #: builtin/bisect.c
 msgid "git bisect (good|bad) [<rev>...]"
@@ -3473,12 +3494,13 @@ 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
+#, 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"
@@ -4488,7 +4510,7 @@ msgstr "不能对 '%s' 执行 reflog 操作:%s\n"
 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"
 
@@ -4757,8 +4779,8 @@ msgid "new-branch"
 msgstr "新分支"
 
 #: builtin/checkout.c
-msgid "new unparented branch"
-msgstr "æ\96°ç\9a\84没æ\9c\89ç\88¶æ\8f\90交的分支"
+msgid "new unborn branch"
+msgstr "æ\96°ç\9a\84æ\9cªè¯\9eç\94\9f的分支"
 
 #: builtin/checkout.c builtin/merge.c
 msgid "update ignored files (default)"
@@ -4824,7 +4846,7 @@ msgstr ""
 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 "分支"
@@ -5065,10 +5087,6 @@ msgid ""
 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 [<选项>] [--] <仓库> [<路径>]"
@@ -5160,6 +5178,7 @@ msgid "create a shallow clone since a specific time"
 msgstr "从一个特定时间创建一个浅克隆"
 
 #: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/rebase.c
+#: builtin/replay.c
 msgid "revision"
 msgstr "版本"
 
@@ -5187,6 +5206,10 @@ msgstr "git目录"
 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"
@@ -5336,11 +5359,10 @@ msgstr "太多参数。"
 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
@@ -5501,7 +5523,7 @@ msgid ""
 "--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 | --"
@@ -5520,17 +5542,17 @@ msgstr "保存图形的对象目录"
 
 #: 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'"
+msgstr "无法打开提交图链 '%s'"
 
 #: builtin/commit-graph.c
 #, c-format
@@ -5574,15 +5596,15 @@ msgstr "启用变更路径的计算"
 
 #: 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"
@@ -8378,6 +8400,11 @@ msgstr "没有线程支持,忽略 %s"
 msgid "unable to read tree (%s)"
 msgstr "无法读取树(%s)"
 
+#: 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"
@@ -8901,11 +8928,6 @@ msgstr "解压缩严重的不一致"
 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"
@@ -9083,11 +9105,13 @@ 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[=<权限>]] [<目录>]"
 
@@ -9246,7 +9270,7 @@ msgid ""
 "<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"
@@ -9990,6 +10014,12 @@ msgstr ""
 "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 "将结果发送到标准输出"
@@ -10018,6 +10048,14 @@ msgstr "如果冲突,使用他们的版本"
 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 "如果冲突,使用指定长度的标记"
@@ -10135,10 +10173,6 @@ msgstr "--trivial-merge 与其他所有选项不兼容"
 msgid "unknown strategy option: -X%s"
 msgstr "未知的策略选项:-X%s"
 
-#: builtin/merge-tree.c
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base 与 --stdin 不兼容"
-
 #: builtin/merge-tree.c builtin/notes.c
 #, c-format
 msgid "malformed input line: '%s'."
@@ -10296,7 +10330,7 @@ msgstr "'%s' 没有指向一个提交"
 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 "不能写入索引。"
 
@@ -10304,7 +10338,7 @@ msgstr "不能写入索引。"
 msgid "Not handling anything other than two heads merge."
 msgstr "未处理两个头合并之外的任何操作。"
 
-#: builtin/merge.c t/helper/test-fast-rebase.c
+#: builtin/merge.c
 #, c-format
 msgid "unable to write %s"
 msgstr "不能写 %s"
@@ -11290,6 +11324,11 @@ msgstr "压缩对象中"
 msgid "inconsistency with delta count"
 msgstr "不一致的差异计数"
 
+#: builtin/pack-objects.c
+#, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "无效的 pack.allowPackReuse 值:'%s'"
+
 #: builtin/pack-objects.c
 #, c-format
 msgid ""
@@ -11614,10 +11653,10 @@ 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>(来自  %<PRIuMAX> 个包)"
 
 #: builtin/pack-redundant.c
 msgid ""
@@ -12691,7 +12730,7 @@ msgstr "没有正在进行的变基?"
 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"
 
@@ -12735,12 +12774,6 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "开关 `C' 期望一个数字值"
 
-#: builtin/rebase.c
-msgid ""
-"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
-"autosquash"
-msgstr "应用的选项与 rebase.autoSquash 不兼容。考虑加上 --no-autosquash"
-
 #: builtin/rebase.c
 msgid ""
 "apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
@@ -13224,8 +13257,8 @@ msgid ""
 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
@@ -13956,6 +13989,84 @@ msgstr "--convert-graft-file 不带参数"
 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]"
@@ -14215,18 +14326,6 @@ msgstr "--prefix 需要一个参数"
 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 "该操作必须在一个工作区中运行"
@@ -14730,11 +14829,6 @@ msgstr "不打印结果到标准输出(例如与 --verify 参数共用)"
 msgid "show refs from stdin that aren't in local repository"
 msgstr "显示从标准输入中读入的不在本地仓库中的引用"
 
-#: builtin/show-ref.c
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "选项 '%s'、'%s' 或 '%s' 只能使用其一"
-
 #: builtin/sparse-checkout.c
 msgid ""
 "git sparse-checkout (init | list | set | add | reapply | disable | check-"
@@ -16509,30 +16603,30 @@ msgstr "没有可用的源分支,将基于 '--orphan' 选项进行推断"
 #: builtin/worktree.c
 #, c-format
 msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"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"
+"分支(没有提交的分支)的工作区,你可以使用\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 orphan branch\n"
+"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"
+"分支(没有提交的分支)的工作区,你可以使用\n"
+"选项 --orphan 来执行此操作:\n"
 "\n"
 "    git worktree add --orphan %s\n"
 
@@ -16601,6 +16695,11 @@ msgstr "不能创建目录 '%s'"
 msgid "initializing"
 msgstr "初始化"
 
+#: 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')"
@@ -16645,11 +16744,6 @@ msgstr ""
 "尽管已配置远程仓库,但不存在任何本地的或远程的引用,操作终止。\n"
 "请先使用 'add -f' 来覆盖或拉取一个远程仓库"
 
-#: builtin/worktree.c
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "'%s' 和 '%s' 不能同时使用"
-
 #: builtin/worktree.c
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "检出 <分支>,即使已经被检出到其它工作区"
@@ -16663,8 +16757,8 @@ msgid "create or reset a branch"
 msgstr "创建或重置一个分支"
 
 #: builtin/worktree.c
-msgid "create unborn/orphaned branch"
-msgstr "创建一个尚未诞生的/孤立的分支"
+msgid "create unborn branch"
+msgstr "创建一个尚未诞生的分支"
 
 #: builtin/worktree.c
 msgid "populate the new working tree"
@@ -16693,12 +16787,8 @@ msgstr "选项 '%s'、'%s' 和 '%s' 不能同时使用"
 
 #: builtin/worktree.c
 #, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "选项 '%s' 与 '%s' 不能同时使用"
-
-#: builtin/worktree.c
-msgid "<commit-ish>"
-msgstr "<提交号>"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "选项 '%s' 和提交号不能同时使用"
 
 #: builtin/worktree.c
 msgid "added with --lock"
@@ -17488,6 +17578,10 @@ msgstr "打包仓库中未打包对象"
 msgid "Create, list, delete refs to replace objects"
 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 "生成待定更改的摘要"
@@ -17803,57 +17897,104 @@ msgstr "一个管理大型 Git 仓库的工具"
 
 #: 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 "æ\8f\90交å\9b¾å½¢å\93\88å¸\8cç\89\88æ\9c¬ %X å\92\8cç\89\88æ\9c¬ %X ä¸\8då\8c¹é\85\8d"
+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 "提交图的基础图形块过小"
+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 "æ\97 æ\95\88ç\9a\84æ\8f\90交ä½\8dç½®ã\80\82æ\8f\90交å\9b¾å½¢å\8f¯è\83½å·²æ\8d\9få\9d\8f"
+msgstr "无效的提交位置。提交图可能已损坏"
 
 #: commit-graph.c
 #, c-format
@@ -17868,6 +18009,10 @@ msgstr "提交图需要溢出世代数据,但是没有"
 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 "正在加载提交图中的已知提交"
@@ -17943,26 +18088,25 @@ msgstr[1] "正在用 %d 步写出提交图"
 
 #: 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>"
+msgstr "无法合并提交图,总共已累加提交数:%<PRIuMAX>,当前待累加提交数:%<PRIuMAX>"
 
 #: commit-graph.c
 #, c-format
 msgid "cannot merge graph %s, too many commits: %<PRIuMAX>"
-msgstr "无法合并提交图 %s, 提交过多:%<PRIuMAX>"
+msgstr "无法合并提交图 %s, 提交过多:%<PRIuMAX>"
 
 #: commit-graph.c
 msgid "Scanning merged commits"
@@ -17970,7 +18114,7 @@ msgstr "正在扫描合并提交"
 
 #: commit-graph.c
 msgid "Merging commit-graph"
-msgstr "正在合并提交图"
+msgstr "正在合并提交图"
 
 #: commit-graph.c
 msgid "attempting to write a commit-graph, but 'core.commitGraph' is disabled"
@@ -17987,59 +18131,59 @@ msgstr "提交图文件的校验码错误,可能已经损坏"
 #: 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"
+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 的提交图父提交列表过早终止"
+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 "æ\8f\90交å\9b¾å½¢å\85·æ\9c\89é\9b¶å\92\8cé\9d\9eé\9b¶ç\9a\84ä¸\96代ï¼\88ä¾\8bå¦\82ï¼\9aæ\8f\90交 '%s' å\92\8c '%s'ï¼\89"
+msgstr "提交图具有零和非零的世代(例如:提交 '%s' 和 '%s')"
 
 #: commit-graph.c
 msgid "Verifying commits in commit graph"
@@ -18073,7 +18217,7 @@ msgstr ""
 #: commit.c
 #, c-format
 msgid "commit %s exists in commit-graph but not in the object database"
-msgstr "提交 %s 存在于提交图中,但不存在于对象数据库中"
+msgstr "提交 %s 存在于提交图中,但不存在于对象数据库中"
 
 #: commit.c
 #, c-format
@@ -19255,6 +19399,11 @@ msgstr "color-moved-ws:allow-indentation-change 不能与其它空白字符模
 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 ""
@@ -19348,13 +19497,6 @@ msgstr "坏的 --color-moved 参数:%s"
 msgid "invalid mode '%s' in --color-moved-ws"
 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"
@@ -19412,8 +19554,8 @@ msgid "output only the last line of --stat"
 msgstr "只输出 --stat 的最后一行"
 
 #: diff.c
-msgid "<param1,param2>..."
-msgstr "<参数1,参数2>..."
+msgid "<param1>,<param2>..."
+msgstr "<参数1>,<参数2>..."
 
 #: diff.c
 msgid ""
@@ -19425,8 +19567,8 @@ 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,<参数1>,<参数2>... 的同义词"
 
 #: diff.c
 msgid "warn if changes introduce conflict markers or whitespace errors"
@@ -19648,14 +19790,6 @@ msgstr "使用 \"patience diff\" 算法生成差异"
 msgid "generate diff using the \"histogram diff\" algorithm"
 msgstr "使用 \"histogram diff\" 算法生成差异"
 
-#: diff.c
-msgid "<algorithm>"
-msgstr "<算法>"
-
-#: diff.c
-msgid "choose a diff algorithm"
-msgstr "选择一个差异算法"
-
 #: diff.c
 msgid "<text>"
 msgstr "<文本>"
@@ -21411,6 +21545,12 @@ msgstr "无法读取缓存"
 msgid "multi-pack-index OID fanout is of the wrong size"
 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 查询块大小错误"
@@ -21469,6 +21609,15 @@ msgstr "多包索引包名无序:'%s' 在 '%s' 之前"
 msgid "bad pack-int-id: %u (%u total packs)"
 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 太小"
@@ -21576,12 +21725,6 @@ msgstr "不正确的校验码"
 msgid "Looking for referenced packfiles"
 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"
@@ -22211,6 +22354,10 @@ msgstr "多包位图缺少必需的反向索引"
 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"
@@ -22234,6 +22381,11 @@ msgstr "损坏的位图查询表:提交索引 %u 超出范围"
 msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
 msgstr "损坏的 EWAH 位图:提交 \"%s\" 位图的文件头被截断"
 
+#: 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"
@@ -22349,6 +22501,10 @@ msgstr "位于 %<PRIu64> 的无效的反向索引:%<PRIu32> != %<PRIu32>"
 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 "无法同时写入和校验反向索引"
@@ -22427,11 +22583,6 @@ msgstr "%s 不可用"
 msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr "%s 期望一个非负整数和一个可选的 k/m/g 后缀"
 
-#: parse-options.c
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s 与 %s 不兼容"
-
 #: parse-options.c
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
@@ -22821,11 +22972,6 @@ msgstr "无法索引文件 '%s'"
 msgid "unable to add '%s' to index"
 msgstr "无法在索引中添加 '%s'"
 
-#: 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"
@@ -23500,17 +23646,12 @@ msgstr "'%s' 已存在,无法创建 '%s'"
 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"
@@ -24665,7 +24806,7 @@ msgstr "无效的作者身份 '%s'"
 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"
@@ -25155,6 +25296,10 @@ msgstr "应用自动贮藏导致冲突。"
 msgid "Autostash exists; creating a new stash entry."
 msgstr "自动贮藏已经存在;正在创建一个新的贮藏条目。"
 
+#: sequencer.c
+msgid "autostash reference is a symref"
+msgstr "自动贮藏的引用是一个符号引用"
+
 #: sequencer.c
 msgid "could not detach HEAD"
 msgstr "不能分离头指针"
@@ -25529,6 +25674,11 @@ msgstr "没有从 '%s' 复制模版:%s"
 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"
@@ -25544,14 +25694,14 @@ msgid "attempt to reinitialize repository with different hash"
 msgstr "尝试用不同的哈希算法重新初始化仓库"
 
 #: setup.c
-#, c-format
-msgid "%s already exists"
-msgstr "%s 已经存在"
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr "尝试使用不同的引用存储格式重新初始化仓库"
 
 #: setup.c
 #, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init:已忽略 --initial-branch=%s"
+msgid "%s already exists"
+msgstr "%s 已经存在"
 
 #: setup.c
 #, c-format
@@ -25869,14 +26019,6 @@ msgstr "在每次迭代前清除缓存树"
 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"
@@ -26070,10 +26212,6 @@ msgstr "协议不支持设置远程服务路径"
 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"
@@ -26237,11 +26375,6 @@ msgstr "不能解析 transport.color.* 配置"
 msgid "support for protocol v2 not implemented yet"
 msgstr "协议 v2 的支持尚未实现"
 
-#: transport.c
-#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "配置 '%s' 的取值未知:%s"
-
 #: transport.c
 #, c-format
 msgid "transport '%s' not allowed"
@@ -26301,6 +26434,10 @@ msgstr "协议不支持 bundle-uri 操作"
 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 "太短的树对象"
@@ -27329,6 +27466,11 @@ msgstr "另外,您的索引中包含未提交的变更。"
 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 "
index f777a0596f8caa04151ce928cf253090fcdd4557..312dd128a41163e63869696369138c93aefa43d9 100644 (file)
 # - Yichao Yu <yyc1992 AT gmail.com>
 # - Zhuang Ya <zhuangya AT me.com>
 #
-# Yi-Jyun Pan <pan93412@gmail.com>, 2021, 2022, 2023.
+# Yi-Jyun Pan <pan93412@gmail.com>, 2021, 2022, 2023, 2024.
 # Kaiyang Wu <self@origincode.me>, 2022.
-# lumynou5 <lumynou5.tw@gmail.com>, 2023.
+# 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: 2023-11-19 22:29+0800\n"
-"PO-Revision-Date: 2023-11-19 23:34+0800\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"
@@ -36,7 +36,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Poedit 3.4.1\n"
+"X-Generator: Poedit 3.4.2\n"
 "X-ZhConverter: 繁化姬 dict-f4bc617e-r910 @ 2019/11/16 20:23:12 | https://"
 "zhconvert.org\n"
 
@@ -910,7 +910,7 @@ msgid "unclosed quote"
 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 "引數過多"
 
@@ -925,12 +925,13 @@ msgid "unrecognized whitespace ignore option '%s'"
 msgstr "空白字元忽略選項 “%s” 無法識別"
 
 #: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout-index.c
-#: builtin/checkout.c builtin/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/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
@@ -1240,12 +1241,12 @@ msgstr "%s:已存在於工作區中"
 #: 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
@@ -1732,6 +1733,11 @@ msgstr "“%s” 選項需要 “%s”"
 msgid "Unexpected option --output"
 msgstr "非預期選項 --output"
 
+#: archive.c
+#, c-format
+msgid "extra command line parameter '%s'"
+msgstr "多出命令列參數 “%s”"
+
 #: archive.c
 #, c-format
 msgid "Unknown archive format '%s'"
@@ -1788,6 +1794,17 @@ msgstr "忽略過大的 gitattributes 資料物件 “%s”"
 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"
@@ -2323,8 +2340,8 @@ 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”"
@@ -2477,8 +2494,7 @@ msgstr "git write-tree 無法寫入樹狀物件"
 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 "無法寫入提交物件"
 
@@ -2661,8 +2677,9 @@ msgid "n"
 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"
 
@@ -3357,12 +3374,13 @@ 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
+#, 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"
@@ -4370,7 +4388,7 @@ msgstr "無法對 “%s” 執行 reflog 動作:%s\n"
 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"
 
@@ -4631,8 +4649,8 @@ msgid "new-branch"
 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)"
@@ -4699,7 +4717,7 @@ msgstr ""
 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 "branch"
@@ -4940,10 +4958,6 @@ msgid ""
 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 [<options>] [--] <repo> [<dir>]"
@@ -5035,6 +5049,7 @@ msgid "create a shallow clone since a specific time"
 msgstr "建立從指定時間到現在的淺層複製"
 
 #: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/rebase.c
+#: builtin/replay.c
 msgid "revision"
 msgstr "revision"
 
@@ -5062,6 +5077,10 @@ msgstr "gitdir"
 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"
@@ -5211,11 +5230,10 @@ msgstr "太多參數。"
 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
@@ -5376,14 +5394,14 @@ msgid ""
 "--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"
@@ -8262,6 +8280,11 @@ msgstr "沒有執行緒支援,忽略 %s"
 msgid "unable to read tree (%s)"
 msgstr "無法讀取樹(%s)"
 
+#: 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"
@@ -8784,11 +8807,6 @@ msgstr "解壓縮嚴重的不一致"
 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"
@@ -8962,11 +8980,13 @@ 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=<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>]"
 
@@ -9125,7 +9145,7 @@ msgid ""
 "<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"
@@ -9868,6 +9888,13 @@ msgstr ""
 "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 "將結果傳送到標準輸出"
@@ -9896,6 +9923,14 @@ msgstr "如果衝突,使用他們的版本"
 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 "如果衝突,使用指定長度的標記"
@@ -10012,10 +10047,6 @@ msgstr "--trivial-merge 和其他所有選項都不相容"
 msgid "unknown strategy option: -X%s"
 msgstr "未知的策略選項:-X%s"
 
-#: builtin/merge-tree.c
-msgid "--merge-base is incompatible with --stdin"
-msgstr "--merge-base 與 --stdin 不相容"
-
 #: builtin/merge-tree.c builtin/notes.c
 #, c-format
 msgid "malformed input line: '%s'."
@@ -10174,7 +10205,7 @@ msgstr "'%s' 沒有指向一個提交"
 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 "不能寫入索引。"
 
@@ -10182,7 +10213,7 @@ msgstr "不能寫入索引。"
 msgid "Not handling anything other than two heads merge."
 msgstr "未處理兩個頭合併之外的任何動作。"
 
-#: builtin/merge.c t/helper/test-fast-rebase.c
+#: builtin/merge.c
 #, c-format
 msgid "unable to write %s"
 msgstr "不能寫 %s"
@@ -11168,6 +11199,11 @@ msgstr "壓縮物件中"
 msgid "inconsistency with delta count"
 msgstr "不一致的差異計數"
 
+#: builtin/pack-objects.c
+#, c-format
+msgid "invalid pack.allowPackReuse value: '%s'"
+msgstr "無效的 pack.allowPackReuse 值:“%s”"
+
 #: builtin/pack-objects.c
 #, c-format
 msgid ""
@@ -11492,10 +11528,10 @@ 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> (總共 %<PRIuMAX>)"
 
 #: builtin/pack-redundant.c
 msgid ""
@@ -12574,7 +12610,7 @@ msgstr "沒有正在進行的重定基底?"
 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"
 
@@ -12618,12 +12654,6 @@ msgstr ""
 msgid "switch `C' expects a numerical value"
 msgstr "開關 `C' 期望一個數字值"
 
-#: builtin/rebase.c
-msgid ""
-"apply options are incompatible with rebase.autoSquash.  Consider adding --no-"
-"autosquash"
-msgstr "apply 選項與 rebase.autoSquash 不相容。請考慮加上 --no-autosquash"
-
 #: builtin/rebase.c
 msgid ""
 "apply options are incompatible with rebase.rebaseMerges.  Consider adding --"
@@ -13109,7 +13139,7 @@ msgid ""
 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
@@ -13838,6 +13868,86 @@ msgstr "--convert-graft-file 不帶參數"
 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]"
@@ -14100,18 +14210,6 @@ msgstr "--prefix 需要 1 個引數"
 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 "該動作必須在一個工作區中執行"
@@ -14610,11 +14708,6 @@ msgstr "不列印結果到標準輸出(例如與 --verify 參數共用)"
 msgid "show refs from stdin that aren't in local repository"
 msgstr "顯示從標準輸入中讀入的不在本機版本庫中的引用"
 
-#: builtin/show-ref.c
-#, c-format
-msgid "only one of '%s', '%s' or '%s' can be given"
-msgstr "只能傳入 “%s”、“%s” 或 “%s”"
-
 #: builtin/sparse-checkout.c
 msgid ""
 "git sparse-checkout (init | list | set | add | reapply | disable | check-"
@@ -16389,14 +16482,14 @@ msgstr "沒有可能的來源分支,推測為 “--orphan”"
 #: builtin/worktree.c
 #, c-format
 msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"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"
+"未誕生分支(即沒有提交的分支),可以使用 --orphan 達到\n"
 "這個效果:\n"
 "\n"
 "    git worktree add --orphan -b %s %s\n"
@@ -16404,14 +16497,14 @@ msgstr ""
 #: builtin/worktree.c
 #, c-format
 msgid ""
-"If you meant to create a worktree containing a new orphan branch\n"
+"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"
+"未誕生分支(即沒有提交的分支),可以使用 --orphan 達到\n"
 "這個效果:\n"
 "\n"
 "    git worktree add --orphan %s\n"
@@ -16481,6 +16574,11 @@ msgstr "不能建立目錄 '%s'"
 msgid "initializing"
 msgstr "正在初始化"
 
+#: 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')"
@@ -16525,11 +16623,6 @@ msgstr ""
 "即使有提供一個遠端,卻不存在本機或遠端引用,\n"
 "故停止。使用 “add -f” 先覆蓋或抓取遠端"
 
-#: builtin/worktree.c
-#, c-format
-msgid "'%s' and '%s' cannot be used together"
-msgstr "無法同時使用 “%s” 和 “%s”"
-
 #: builtin/worktree.c
 msgid "checkout <branch> even if already checked out in other worktree"
 msgstr "簽出 <分支>,即使已經被簽出到其它工作區"
@@ -16543,8 +16636,8 @@ msgid "create or reset a branch"
 msgstr "建立或重設一個分支"
 
 #: builtin/worktree.c
-msgid "create unborn/orphaned branch"
-msgstr "建立尚無內容(孤立)的分支"
+msgid "create unborn branch"
+msgstr "建立未誕生分支"
 
 #: builtin/worktree.c
 msgid "populate the new working tree"
@@ -16573,12 +16666,8 @@ msgstr "「%s」、「%s」和「%s」選項不得同時使用"
 
 #: builtin/worktree.c
 #, c-format
-msgid "options '%s', and '%s' cannot be used together"
-msgstr "無法同時使用 “%s” 和 “%s” 選項"
-
-#: builtin/worktree.c
-msgid "<commit-ish>"
-msgstr "<提交指示元>"
+msgid "option '%s' and commit-ish cannot be used together"
+msgstr "“%s” 選項和提交號不得同時使用"
 
 #: builtin/worktree.c
 msgid "added with --lock"
@@ -17366,6 +17455,10 @@ msgstr "打包版本庫中未打包物件"
 msgid "Create, list, delete refs to replace objects"
 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 "生成待定更改的摘要"
@@ -17683,6 +17776,37 @@ msgstr "用來管理大型 Git 版本庫的工具"
 msgid "commit-graph file is too small"
 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"
@@ -17701,7 +17825,19 @@ msgstr "提交圖形雜湊版本 %X 和版本 %X 不符合"
 #: 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"
@@ -17720,10 +17856,14 @@ msgstr "提交圖形鏈不符合"
 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"
@@ -17746,6 +17886,10 @@ msgstr "提交圖需要比目前更多的世代資料,但沒有相關資料"
 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 "正在載入提交圖中的已知提交"
@@ -19120,6 +19264,11 @@ msgstr "color-moved-ws:allow-indentation-change 不能與其它空白字元模
 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 ""
@@ -19213,13 +19362,6 @@ msgstr "壞的 --color-moved 參數:%s"
 msgid "invalid mode '%s' in --color-moved-ws"
 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"
@@ -19277,8 +19419,9 @@ msgid "output only the last line of --stat"
 msgstr "只輸出 --stat 的最後一行"
 
 #: diff.c
-msgid "<param1,param2>..."
-msgstr "<參數1,參數2>..."
+#| msgid "<param1,param2>..."
+msgid "<param1>,<param2>..."
+msgstr "<param1>,<param2>..."
 
 #: diff.c
 msgid ""
@@ -19290,8 +19433,8 @@ 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"
@@ -19513,14 +19656,6 @@ msgstr "使用 \"patience diff\" 演算法生成差異"
 msgid "generate diff using the \"histogram diff\" algorithm"
 msgstr "使用 \"histogram diff\" 演算法生成差異"
 
-#: diff.c
-msgid "<algorithm>"
-msgstr "<演算法>"
-
-#: diff.c
-msgid "choose a diff algorithm"
-msgstr "選擇一個差異演算法"
-
 #: diff.c
 msgid "<text>"
 msgstr "<文字>"
@@ -20940,7 +21075,7 @@ msgstr ""
 #. 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"
 #.
 #: merge-ort.c
 #, c-format
@@ -21274,6 +21409,12 @@ msgstr "讀取快取失敗"
 msgid "multi-pack-index OID fanout is of the wrong size"
 msgstr "多包索引的物件 ID fanout 大小錯誤"
 
+#: 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 查詢區塊的大小有誤"
@@ -21332,6 +21473,15 @@ msgstr "多包索引包名無序:'%s' 在 '%s' 之前"
 msgid "bad pack-int-id: %u (%u total packs)"
 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 太小"
@@ -21439,12 +21589,6 @@ msgstr "總和檢查碼不正確"
 msgid "Looking for referenced packfiles"
 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"
@@ -21820,7 +21964,7 @@ msgstr "%s [無效物件]"
 #. 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"
 #.
 #: object-name.c
 #, c-format
@@ -21830,7 +21974,7 @@ msgstr "%s 提交 %s - %s"
 #. 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.
@@ -21847,7 +21991,7 @@ msgstr "%s 標籤 %s - %s"
 #. 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]"
 #.
 #: object-name.c
 #, c-format
@@ -22075,6 +22219,10 @@ msgstr "多包位圖缺少需要的反向索引"
 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"
@@ -22098,6 +22246,11 @@ msgstr "位圖查詢表損壞:提交索引 %u 超出範圍"
 msgid "corrupt ewah bitmap: truncated header for bitmap of commit \"%s\""
 msgstr "ewah 位圖損壞:提交 “%s” 之位圖的標頭遭截斷"
 
+#: 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"
@@ -22213,6 +22366,10 @@ msgstr "%<PRIu64> 位置的修訂版索引 (rev-index) 無效:%<PRIu32> != %<P
 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 "無法同時寫入和驗證倒排索引"
@@ -22291,11 +22448,6 @@ msgstr "%s 不可用"
 msgid "%s expects a non-negative integer value with an optional k/m/g suffix"
 msgstr "%s 期望一個非負整數和一個可選的 k/m/g 後綴"
 
-#: parse-options.c
-#, c-format
-msgid "%s is incompatible with %s"
-msgstr "%s 與 %s 不相容"
-
 #: parse-options.c
 #, c-format
 msgid "ambiguous option: %s (could be --%s%s or --%s%s)"
@@ -22685,11 +22837,6 @@ msgstr "無法索引檔案 '%s'"
 msgid "unable to add '%s' to index"
 msgstr "無法在索引中新增 '%s'"
 
-#: 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"
@@ -23364,17 +23511,12 @@ msgstr "'%s' 已存在,無法建立 '%s'"
 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"
@@ -24532,7 +24674,7 @@ msgstr "無效的作者身分 '%s'"
 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"
@@ -25023,6 +25165,10 @@ msgstr "因套用自動貯存而導致衝突。"
 msgid "Autostash exists; creating a new stash entry."
 msgstr "已有自動貯存;建立新貯存項目。"
 
+#: sequencer.c
+msgid "autostash reference is a symref"
+msgstr "autostash 引用是符號引用"
+
 #: sequencer.c
 msgid "could not detach HEAD"
 msgstr "不能分離開頭指標"
@@ -25395,6 +25541,11 @@ msgstr "沒有從 '%s' 複製範本:%s"
 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"
@@ -25410,14 +25561,14 @@ msgid "attempt to reinitialize repository with different hash"
 msgstr "嘗試以不同的雜湊值重新初始化版本庫"
 
 #: setup.c
-#, c-format
-msgid "%s already exists"
-msgstr "%s 已經存在"
+msgid ""
+"attempt to reinitialize repository with different reference storage format"
+msgstr "嘗試以不同的引用儲存格式重新初始化版本庫"
 
 #: setup.c
 #, c-format
-msgid "re-init: ignored --initial-branch=%s"
-msgstr "re-init: 忽略 --initial-branch=%s"
+msgid "%s already exists"
+msgstr "%s 已經存在"
 
 #: setup.c
 #, c-format
@@ -25733,14 +25884,6 @@ msgstr "每次迭代前清除快取樹狀物件"
 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"
@@ -25934,10 +26077,6 @@ msgstr "協定不支援設定遠端服務路徑"
 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"
@@ -26101,11 +26240,6 @@ msgstr "不能解析 transport.color.* 設定"
 msgid "support for protocol v2 not implemented yet"
 msgstr "協定 v2 的支援尚未實現"
 
-#: transport.c
-#, c-format
-msgid "unknown value for config '%s': %s"
-msgstr "設定 '%s' 的取值未知:%s"
-
 #: transport.c
 #, c-format
 msgid "transport '%s' not allowed"
@@ -26165,6 +26299,10 @@ msgstr "通訊協定不支援 bundle-uri 動作"
 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 "太短的樹狀物件"
@@ -27193,6 +27331,11 @@ msgstr "另外,您的索引中包含未提交的變更。"
 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 "
@@ -27649,3 +27792,55 @@ msgstr "略過 %s 含備份後綴 '%s'。\n"
 #, perl-format
 msgid "Do you really want to send %s? [y|N]: "
 msgstr "您真的要傳送 %s?[y|N]: "
+
+#~ msgid "-x and -X cannot be used together"
+#~ msgstr "-x 和 -X 不能同時使用"
+
+#~ msgid ""
+#~ "--bundle-uri is incompatible with --depth, --shallow-since, and --shallow-"
+#~ "exclude"
+#~ msgstr ""
+#~ "--bundle-uri 與 --depth、--shallow-since 和 --shallow-exclude 不相容"
+
+#~ msgid "--merge-base is incompatible with --stdin"
+#~ msgstr "--merge-base 與 --stdin 不相容"
+
+#~ msgid ""
+#~ "apply options are incompatible with rebase.autoSquash.  Consider adding --"
+#~ "no-autosquash"
+#~ msgstr "apply 選項與 rebase.autoSquash 不相容。請考慮加上 --no-autosquash"
+
+#~ 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 同時使用"
+
+#, c-format
+#~ msgid "only one of '%s', '%s' or '%s' can be given"
+#~ msgstr "只能傳入 “%s”、“%s” 或 “%s”"
+
+#, c-format
+#~ msgid "'%s' and '%s' cannot be used together"
+#~ msgstr "無法同時使用 “%s” 和 “%s”"
+
+#, c-format
+#~ msgid "options '%s', and '%s' cannot be used together"
+#~ msgstr "無法同時使用 “%s” 和 “%s” 選項"
+
+#~ 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 "未處理選項"
index f29b06a5d059e23030333f26d5ba0667872251ab..3b85add243ba79a4ccc90c9faacbb9fee2742303 100644 (file)
@@ -17,6 +17,7 @@
 #include "pack-mtimes.h"
 #include "config.h"
 #include "run-command.h"
+#include "sequencer.h"
 
 struct connectivity_progress {
        struct progress *progress;
@@ -30,6 +31,52 @@ static void update_progress(struct connectivity_progress *cp)
                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)
 {
@@ -322,6 +369,9 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
        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);
index 35b989e1dfe59e9d274afead3d397f97ed624b26..0ec29f7385480ad4ee71a6cc684307b2bf19c814 100644 (file)
@@ -1611,6 +1611,12 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
        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)
@@ -2622,6 +2628,12 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
                                       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
@@ -2744,6 +2756,9 @@ static int ref_kind_from_refname(const char *refname)
                        return ref_kind[i].kind;
        }
 
+       if (is_pseudoref(get_main_ref_store(the_repository), refname))
+               return FILTER_REFS_PSEUDOREFS;
+
        return FILTER_REFS_OTHERS;
 }
 
@@ -2775,7 +2790,16 @@ static struct ref_array_item *apply_ref_filter(const char *refname, const struct
 
        /* Obtain the current ref kind from filter_ref_kind() and ignore unwanted refs. */
        kind = filter_ref_kind(filter, refname);
-       if (!(kind & filter->kind))
+
+       /*
+        * 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))
@@ -3041,9 +3065,15 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref
                        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_ALL)
+               else if (filter->kind & FILTER_REFS_REGULAR)
                        ret = for_each_fullref_in_pattern(filter, fn, cb_data);
-               if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
+
+               /*
+                * 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);
        }
 
index 07cd6f6da3da7e3950dc77538baf0f71950630e1..0ca28d2bba6f2aed4b8d084972739f3a88f44caa 100644 (file)
 #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;
diff --git a/refs.c b/refs.c
index 20e8f1ff1f114ab0dff0927a4456fbca639c6fb3..55d2e0b2cb9e959443e98eb329fdf97eff9073a9 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -35,6 +35,7 @@
  */
 static const struct ref_storage_be *refs_backends[] = {
        [REF_STORAGE_FORMAT_FILES] = &refs_be_files,
+       [REF_STORAGE_FORMAT_REFTABLE] = &refs_be_reftable,
 };
 
 static const struct ref_storage_be *find_ref_storage_backend(unsigned int ref_storage_format)
@@ -859,6 +860,47 @@ static int is_pseudoref_syntax(const char *refname)
        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);
 }
@@ -1038,55 +1080,40 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
                           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,
@@ -1098,7 +1125,7 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
 
        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;
@@ -1121,14 +1148,24 @@ int read_ref_at(struct ref_store *refs, const char *refname,
        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
@@ -1593,10 +1630,6 @@ struct ref_iterator *refs_ref_iterator_begin(
        if (trim)
                iter = prefix_ref_iterator_begin(iter, "", trim);
 
-       /* Sanity check for subclasses: */
-       if (!iter->ordered)
-               BUG("reference iterator is not ordered");
-
        return iter;
 }
 
@@ -1723,6 +1756,13 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
        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;
@@ -1839,13 +1879,10 @@ done:
 static int is_special_ref(const char *refname)
 {
        /*
-        * Special references get written and read directly via the filesystem
-        * by the subsystems that create them. Thus, they must not go through
-        * the reference backend but must instead be read directly. It is
-        * arguable whether this behaviour is sensible, or whether it's simply
-        * a leaky abstraction enabled by us only having a single reference
-        * backend implementation. But at least for a subset of references it
-        * indeed does make sense to treat them specially:
+        * 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.
@@ -1853,30 +1890,12 @@ static int is_special_ref(const char *refname)
         * - MERGE_HEAD may contain multiple object IDs when merging multiple
         *   heads.
         *
-        * There are some exceptions that you might expect to see on this list
-        * but which are handled exclusively via the reference backend:
-        *
-        * - BISECT_EXPECTED_REV
-        *
-        * - CHERRY_PICK_HEAD
-        *
-        * - HEAD
-        *
-        * - ORIG_HEAD
-        *
-        * - "rebase-apply/" and "rebase-merge/" contain all of the state for
-        *   rebases, including some reference-like files. These are
-        *   exclusively read and written via the filesystem and never go
-        *   through the refdb.
-        *
-        * Writing or deleting references must consistently go either through
-        * the filesystem (special refs) or through the reference backend
-        * (normal ones).
+        * 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[] = {
-               "AUTO_MERGE",
                "FETCH_HEAD",
-               "MERGE_AUTOSTASH",
                "MERGE_HEAD",
        };
        size_t i;
@@ -1997,11 +2016,9 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
 }
 
 /* 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,
@@ -2538,18 +2555,33 @@ cleanup:
        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);
 }
diff --git a/refs.h b/refs.h
index 11b3b6cceafc1898eaf14cf80782d32061292ae9..298caf6c6184cc3a23acf78d1a0e3dc8c7d8614c 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -126,7 +126,9 @@ int should_autocreate_reflog(const char *refname);
 
 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
@@ -396,6 +398,12 @@ int for_each_namespaced_ref(const char **exclude_patterns,
 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/'.
@@ -438,7 +446,20 @@ int refs_create_reflog(struct ref_store *refs, const char *refname,
                       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,
@@ -532,12 +553,19 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat
 /* youngest entry first */
 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
@@ -1021,4 +1049,7 @@ extern struct ref_namespace_info ref_namespace[NAMESPACE__COUNT];
  */
 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 */
index b9775f2c37c7eb5e9de275fc3d6ad2125802eeef..c7531b17f096c329353caf1fd456c726cded5ab8 100644 (file)
@@ -33,10 +33,10 @@ struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_stor
        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;
 }
@@ -181,7 +181,6 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
                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;
@@ -222,7 +221,7 @@ debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
                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);
index b288fc97dba7b4986ccef25cdd1608eb39e349b9..a098d14ea00ed6db449e5d3cc8dff28594228977 100644 (file)
@@ -229,6 +229,38 @@ static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dir
        }
 }
 
+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
@@ -257,8 +289,6 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
        strbuf_add(&refname, dirname, dirnamelen);
 
        while ((de = readdir(d)) != NULL) {
-               struct object_id oid;
-               int flag;
                unsigned char dtype;
 
                if (de->d_name[0] == '.')
@@ -274,33 +304,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
                                         create_dir_entry(dir->cache, refname.buf,
                                                          refname.len));
                } else if (dtype == DT_REG) {
-                       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));
+                       loose_fill_ref_dir_regular_file(refs, refname.buf, dir);
                }
                strbuf_setlen(&refname, dirnamelen);
        }
@@ -311,9 +315,59 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
        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
@@ -324,12 +378,17 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
                /* 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;
 }
@@ -857,7 +916,7 @@ static struct ref_iterator *files_ref_iterator_begin(
         * 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);
 
        /*
@@ -879,8 +938,7 @@ static struct ref_iterator *files_ref_iterator_begin(
 
        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;
@@ -1218,7 +1276,7 @@ static int files_pack_refs(struct ref_store *ref_store,
 
        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) {
                /*
@@ -2116,10 +2174,8 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,
 
 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)
@@ -2130,25 +2186,13 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
        int ok;
 
        while ((ok = dir_iterator_advance(diter)) == ITER_OK) {
-               int flags;
-
                if (!S_ISREG(diter->st.st_mode))
                        continue;
-               if (diter->basename[0] == '.')
-                       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);
+               if (check_refname_format(diter->basename,
+                                        REFNAME_ALLOW_ONELEVEL))
                        continue;
-               }
 
                iter->base.refname = diter->relative_path;
-               iter->base.oid = &iter->oid;
-               iter->base.flags = flags;
                return ITER_OK;
        }
 
@@ -2193,7 +2237,7 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
 
        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();
@@ -2202,7 +2246,7 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
        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);
@@ -2210,32 +2254,6 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
        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 =
@@ -2246,9 +2264,9 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st
                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);
        }
 }
 
@@ -3218,21 +3236,46 @@ static int files_reflog_expire(struct ref_store *ref_store,
        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;
index 6b680f610efbee4e144cfa41763f45834b39d210..9db8b056d56d190fe9ea2087bc327f0154b6ecd2 100644 (file)
@@ -25,11 +25,9 @@ int ref_iterator_abort(struct ref_iterator *ref_iterator)
 }
 
 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 ref_iterator *empty_ref_iterator_begin(void)
        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 merge_ref_iterator {
        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 @@ static struct ref_iterator_vtable merge_ref_iterator_vtable = {
 };
 
 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 @@ struct ref_iterator *merge_ref_iterator_begin(
         * 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 @@ struct ref_iterator *overlay_ref_iterator_begin(
        } 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 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
 
                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 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
        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);
index bf04b933815955bc13109faf3e7916fc769fff15..4e826c05ff2b34986a5844cd40734b10b0298204 100644 (file)
@@ -1111,7 +1111,7 @@ static struct ref_iterator *packed_ref_iterator_begin(
 
        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);
@@ -1245,6 +1245,7 @@ static const char PACKED_REFS_HEADER[] =
        "# 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. */
index a372a00941fda659cc60722452c8766f9c7de455..9f9797209a4545576f3ce5c9a9327c47e3319df1 100644 (file)
@@ -486,7 +486,7 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
 
        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;
index 8e9f04cc670baeddd68b933ee41d11062b2fbe32..56641aa57a138da17037307d37e1ca28baa2a1ee 100644 (file)
@@ -260,6 +260,12 @@ enum do_for_each_ref_flags {
         * 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),
 };
 
 /*
@@ -312,13 +318,6 @@ enum do_for_each_ref_flags {
  */
 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;
@@ -386,15 +385,22 @@ typedef enum iterator_selection ref_iterator_select_fn(
                struct ref_iterator *iter0, struct ref_iterator *iter1,
                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);
 
@@ -423,8 +429,6 @@ struct ref_iterator *overlay_ref_iterator_begin(
  * 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,
@@ -435,14 +439,11 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
 /*
  * 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
@@ -529,7 +530,9 @@ typedef struct ref_store *ref_store_init_fn(struct repository *repo,
                                            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,
@@ -691,6 +694,7 @@ struct ref_storage_be {
 };
 
 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 (file)
index 0000000..eaa8296
--- /dev/null
@@ -0,0 +1,2249 @@
+#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 clear_reftable_log_record(struct reftable_log_record *log)
+{
+       switch (log->value_type) {
+       case REFTABLE_LOG_UPDATE:
+               /*
+                * When we write log records, the hashes are owned by the
+                * caller and thus shouldn't be free'd.
+                */
+               log->value.update.old_hash = NULL;
+               log->value.update.new_hash = NULL;
+               break;
+       case REFTABLE_LOG_DELETION:
+               break;
+       }
+       reftable_log_record_release(log);
+}
+
+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;
+       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 &&
+                   strncmp(iter->prefix, iter->ref.refname, strlen(iter->prefix))) {
+                       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->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;
+
+       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);
+                       log->value.update.new_hash = u->new_oid.hash;
+                       log->value.update.old_hash = tx_update->current_oid.hash;
+                       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++)
+               clear_reftable_log_record(&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;
+
+       ret = reftable_stack_compact_all(stack, NULL);
+       if (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);
+       log.value.update.new_hash = new_oid.hash;
+       if (refs_resolve_ref_unsafe(&create->refs->base, create->refname,
+                                   RESOLVE_REF_READING, &old_oid, NULL))
+               log.value.update.old_hash = old_oid.hash;
+
+       ret = reftable_writer_add_log(writer, &log);
+       clear_reftable_log_record(&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);
+               logs[logs_nr].value.update.old_hash = old_ref.value.val1;
+               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);
+       logs[logs_nr].value.update.new_hash = old_ref.value.val1;
+       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;
+               if (logs[i].value.update.old_hash == old_ref.value.val1)
+                       logs[i].value.update.old_hash = NULL;
+               if (logs[i].value.update.new_hash == old_ref.value.val1)
+                       logs[i].value.update.new_hash = NULL;
+               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;
+       char *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 (iter->last_name && !strcmp(iter->log.refname, iter->last_name))
+                       continue;
+
+               if (check_refname_format(iter->log.refname,
+                                        REFNAME_ALLOW_ONELEVEL))
+                       continue;
+
+               free(iter->last_name);
+               iter->last_name = xstrdup(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);
+       free(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);
+       iter->refs = refs;
+
+       ret = refs->err;
+       if (ret)
+               goto done;
+
+       ret = reftable_stack_reload(refs->main_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)
+                               dest->value.update.old_hash = last_hash;
+                       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,
+};
index f761e48028c4490e40728fd2879a7520297a0e41..0785aff941bdc2d84181651a48f35cfaaefaede9 100644 (file)
@@ -64,12 +64,11 @@ void free_names(char **a)
        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 +88,13 @@ void parse_names(char *buf, int size, char ***namesp)
                        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;
 }
index 096b36862b9f4ebf14be7c3074d6f86d5e92c22c..91f3533efee501faf3e461f7206dbc7a964da98e 100644 (file)
@@ -44,14 +44,27 @@ void parse_names(char *buf, int size, char ***namesp);
 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;
index 1df3d8a0f09671c74143e655cb2963adda872545..72eb73b3806d623010c0e224f4f8ce77d555ce43 100644 (file)
@@ -51,12 +51,7 @@ static int block_writer_register_restart(struct block_writer *w, int n,
        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_writer_finish(struct block_writer *w)
                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;
+
+               REFTABLE_ALLOC_ARRAY(compressed, dest_cap);
 
-               uint8_t *compressed = reftable_malloc(dest_cap);
                while (1) {
                        uLongf out_dest_len = dest_cap;
                        int zresult = compress2(compressed, &out_dest_len,
@@ -206,9 +203,9 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,
                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);
@@ -342,8 +339,7 @@ int block_iter_next(struct block_iter *it, struct reftable_record *rec)
                return -1;
        string_view_consume(&in, n);
 
-       strbuf_reset(&it->last_key);
-       strbuf_addbuf(&it->last_key, &it->key);
+       strbuf_swap(&it->last_key, &it->key);
        it->next_off += start.len - in.len;
        return 0;
 }
@@ -385,23 +381,23 @@ int block_reader_seek(struct block_reader *br, struct block_iter *it,
                .key = *want,
                .r = br,
        };
-       struct reftable_record rec = reftable_new_record(block_reader_type(br));
-       int err = 0;
        struct block_iter next = BLOCK_ITER_INIT;
+       struct reftable_record rec;
+       int err = 0, i;
 
-       int i = binsearch(br->restart_count, &restart_key_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 {
+       i = binsearch(br->restart_count, &restart_key_less, &args);
+       if (i > 0)
+               it->next_off = block_reader_restart_offset(br, i - 1);
+       else
                it->next_off = br->header_off + 4;
-       }
+       it->br = br;
+
+       reftable_record_init(&rec, block_reader_type(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.
index dedb05c7d8c0560898b03aba93043c7efa46aeea..e162c6e33fa00af4e2b3b653c0d6210aea57c803 100644 (file)
@@ -36,7 +36,7 @@ static void test_block_read_write(void)
        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,
index a1ea3044292ef2032485bad261ffa5ed178421bc..eeed254ba9c2da51177eb7ed81fbf16d17ba183b 100644 (file)
@@ -29,7 +29,7 @@ static int strbuf_read_block(void *v, struct reftable_block *dest, uint64_t off,
 {
        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 reftable_block_source malloc_block_source(void)
 }
 
 struct file_block_source {
-       int fd;
        uint64_t size;
+       unsigned char *data;
 };
 
 static uint64_t file_size(void *b)
@@ -87,19 +87,12 @@ static uint64_t file_size(void *b)
 
 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 @@ static int file_read_block(void *v, struct reftable_block *dest, uint64_t off,
 {
        struct file_block_source *b = v;
        assert(off + size <= b->size);
-       dest->data = reftable_malloc(size);
-       if (pread_in_full(b->fd, dest->data, size, off) != size)
-               return -1;
+       dest->data = b->data + off;
        dest->len = size;
        return size;
 }
@@ -125,26 +116,26 @@ static struct reftable_block_source_vtable file_vtable = {
 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;
index a8d174c040658edda3d2c45a9df2f692aef41c9d..8b5ebf6183a97f3ea16a0773880ccd3b835ef68e 100644 (file)
@@ -160,8 +160,7 @@ int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest,
                               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;
index c258ce953e81d7df5774a03250b056280beccbf3..1aa6cd31b74954da9b1b5140cd350459c37c1378 100644 (file)
@@ -19,24 +19,23 @@ https://developers.google.com/open-source/licenses/bsd
 
 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) {
+       for (size_t i = 0; i < mi->stack_len; i++) {
+               struct pq_entry e = {
+                       .index = i,
+               };
+               int err;
+
+               reftable_record_init(&e.rec, mi->typ);
+               err = iterator_next(&mi->stack[i], &e.rec);
+               if (err < 0)
                        return 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_release(&e.rec);
+                       continue;
                }
+
+               merged_iter_pqueue_add(&mi->pq, &e);
        }
 
        return 0;
@@ -45,24 +44,23 @@ static int merged_iter_init(struct merged_iter *mi)
 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++) {
+       for (size_t i = 0; i < mi->stack_len; i++)
                reftable_iterator_destroy(&mi->stack[i]);
-       }
        reftable_free(mi->stack);
-       strbuf_release(&mi->key);
-       strbuf_release(&mi->entry_key);
 }
 
 static int merged_iter_advance_nonnull_subiter(struct merged_iter *mi,
                                               size_t idx)
 {
        struct pq_entry e = {
-               .rec = reftable_new_record(mi->typ),
                .index = idx,
        };
-       int err = iterator_next(&mi->stack[idx], &e.rec);
+       int err;
+
+       reftable_record_init(&e.rec, mi->typ);
+       err = iterator_next(&mi->stack[idx], &e.rec);
        if (err < 0)
                return err;
 
@@ -105,14 +103,19 @@ static int merged_iter_next_entry(struct merged_iter *mi,
          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, &mi->entry_key);
        while (!merged_iter_pqueue_is_empty(mi->pq)) {
                struct pq_entry top = merged_iter_pqueue_top(mi->pq);
-               int cmp = 0;
-
-               reftable_record_key(&top.rec, &mi->key);
+               int cmp;
+
+               /*
+                * When the next entry comes from the same queue as the current
+                * entry then it must by definition be larger. This avoids a
+                * comparison in the most common case.
+                */
+               if (top.index == entry.index)
+                       break;
 
-               cmp = strbuf_cmp(&mi->key, &mi->entry_key);
+               cmp = reftable_record_cmp(&top.rec, &entry.rec);
                if (cmp > 0)
                        break;
 
@@ -168,14 +171,14 @@ static void iterator_from_merged_iter(struct reftable_iterator *it,
 }
 
 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]);
 
@@ -190,7 +193,7 @@ int reftable_new_merged_table(struct reftable_merged_table **dest,
                }
        }
 
-       m = reftable_calloc(sizeof(struct reftable_merged_table));
+       REFTABLE_CALLOC_ARRAY(m, 1);
        m->stack = stack;
        m->stack_len = n;
        m->min = first_min;
@@ -239,50 +242,36 @@ static int merged_table_seek_record(struct reftable_merged_table *mt,
                                    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,
-               .key = STRBUF_INIT,
-               .entry_key = STRBUF_INIT,
        };
-       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.stack, mt->stack_len);
+       for (size_t i = 0; i < mt->stack_len; i++) {
+               err = reftable_table_seek_record(&mt->stack[i],
+                                                &merged.stack[merged.stack_len], 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,
index d5b39dfe7f1e3b54b5dc5e7ae46068155794986f..7d9f95d27ed0a44c4208e743b6b83ba4cdc5536c 100644 (file)
@@ -31,8 +31,6 @@ struct merged_iter {
        uint8_t typ;
        int suppress_deletions;
        struct merged_iter_pqueue pq;
-       struct strbuf key;
-       struct strbuf entry_key;
 };
 
 void merged_table_release(struct reftable_merged_table *mt);
index 46908f738f770f4be920a65df85b96178ddbc70d..d0f77a3b8f51e0abb9af1162106c757d3fc3e553 100644 (file)
@@ -42,7 +42,7 @@ static void write_test_table(struct strbuf *buf,
                }
        }
 
-       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++) {
@@ -70,7 +70,7 @@ static void write_test_log_table(struct strbuf *buf,
                .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++) {
@@ -88,16 +88,17 @@ static struct reftable_merged_table *
 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]);
 
@@ -231,14 +232,10 @@ static void test_merged(void)
        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);
@@ -265,16 +262,17 @@ static struct reftable_merged_table *
 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]);
 
@@ -368,14 +366,10 @@ static void test_merged_logs(void)
        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);
@@ -412,7 +406,7 @@ static void test_default_write_opts(void)
        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",
@@ -420,7 +414,7 @@ static void test_default_write_opts(void)
        };
        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;
index dcefeb793a9051b75a276732a32d88809bb86d94..e0ccce2b9779a8bfd1acac6d42bc7d0e46e3fe0e 100644 (file)
@@ -14,20 +14,9 @@ https://developers.google.com/open-source/licenses/bsd
 
 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;
 }
 
@@ -75,13 +64,9 @@ void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry
 {
        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;
index 011b5c75028a9d8dbad15f23ddb99d39e09ca9cb..c202eff84801820b28c56ddb7de28a2d041f3c08 100644 (file)
@@ -60,7 +60,7 @@ static void test_pq(void)
                if (last) {
                        EXPECT(strcmp(last, rec->u.ref.refname) < 0);
                }
-               // this is names[i], so don't dealloc.
+               /* this is names[i], so don't dealloc. */
                last = rec->u.ref.refname;
                rec->u.ref.refname = NULL;
                reftable_record_release(rec);
index bcb82530d6ce35e8a0a582d9718090067b238899..44b84a125e43b3f0de9460b019854e22835d39e6 100644 (file)
@@ -37,8 +37,9 @@ void reftable_free(void *p)
                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;
index 64dc366fb15935c55c5377ec9d4f1b74640cc3f6..b113daab77336c240d10f64ce9890ff04f5f366a 100644 (file)
@@ -357,24 +357,32 @@ static int table_iter_next(struct table_iter *ti, struct reftable_record *rec)
 
        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);
-               if (err != 0) {
-                       ti->is_finished = 1;
-               }
                table_iter_block_done(ti);
-               if (err != 0) {
+               if (err) {
+                       ti->is_finished = 1;
                        return err;
                }
+
                table_iter_copy_from(ti, &next);
                block_iter_close(&next.bi);
        }
@@ -444,13 +452,13 @@ static int reader_start(struct reftable_reader *r, struct table_iter *ti,
 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) {
@@ -508,8 +516,38 @@ static int reader_seek_indexed(struct reftable_reader *r,
        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)
@@ -539,8 +577,7 @@ static int reader_seek_indexed(struct reftable_reader *r,
 
        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);
@@ -635,8 +672,7 @@ void reader_close(struct reftable_reader *r)
 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;
@@ -711,7 +747,7 @@ static int reftable_reader_refs_for_unindexed(struct reftable_reader *r,
                                              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);
index b8a32240164d6a5c29789fb1730a630127f085d5..363fe0f998b91f72002f819c396893b8086dc992 100644 (file)
@@ -51,12 +51,14 @@ static void write_table(char ***names, struct strbuf *buf, int N,
                .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++) {
                char name[100];
@@ -130,7 +132,7 @@ static void test_log_buffer_size(void)
                                           .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.
@@ -171,7 +173,7 @@ static void test_log_overflow(void)
                                           .message = msg,
                                   } } };
        struct reftable_writer *w =
-               reftable_new_writer(&strbuf_add_void, &buf, &opts);
+               reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 
        uint8_t hash1[GIT_SHA1_RAWSZ]  = {1}, hash2[GIT_SHA1_RAWSZ] = { 2 };
 
@@ -188,7 +190,7 @@ static void test_log_overflow(void)
 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,
@@ -202,7 +204,7 @@ static void test_log_write_read(void)
        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++) {
@@ -294,7 +296,7 @@ static void test_log_zlib_corruption(void)
        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 };
@@ -519,7 +521,7 @@ static void test_table_read_write_seek_index(void)
 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];
 
@@ -535,7 +537,7 @@ static void test_table_refs_for(int indexed)
 
        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;
@@ -628,7 +630,7 @@ static void test_write_empty_table(void)
        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 };
@@ -666,7 +668,7 @@ static void test_write_object_id_min_length(void)
        };
        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 = {
                .update_index = 1,
                .value_type = REFTABLE_REF_VAL1,
@@ -701,7 +703,7 @@ static void test_write_object_id_length(void)
        };
        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 = {
                .update_index = 1,
                .value_type = REFTABLE_REF_VAL1,
@@ -735,7 +737,7 @@ static void test_write_empty_key(void)
        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,
@@ -758,7 +760,7 @@ static void test_write_key_order(void)
        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",
@@ -801,7 +803,7 @@ static void test_write_multiple_indices(void)
        struct reftable_reader *reader;
        int err, i;
 
-       writer = reftable_new_writer(&strbuf_add_void, &writer_buf, &opts);
+       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 = {
@@ -866,6 +868,61 @@ static void test_write_multiple_indices(void)
        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;
@@ -916,5 +973,6 @@ int readwrite_test_main(int argc, const char *argv[])
        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;
 }
index 5c3fbb7b2a1e95ad418066df0aa96235116f5329..d6bb42e887423d95f2fcc1b2b146505ab4bef62a 100644 (file)
@@ -377,10 +377,11 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
 
        assert(hash_size > 0);
 
-       r->refname = reftable_realloc(r->refname, key.len + 1);
+       r->refname = reftable_malloc(key.len + 1);
        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:
@@ -430,7 +431,6 @@ static int reftable_ref_record_is_deletion_void(const void *p)
                (const struct reftable_ref_record *)p);
 }
 
-
 static int reftable_ref_record_equal_void(const void *a,
                                          const void *b, int hash_size)
 {
@@ -439,6 +439,13 @@ static int reftable_ref_record_equal_void(const void *a,
        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)
 {
@@ -455,6 +462,7 @@ static struct reftable_record_vtable reftable_ref_record_vtable = {
        .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,
 };
 
@@ -497,12 +505,13 @@ static void reftable_obj_record_copy_from(void *rec, const void *src_rec,
                (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);
 }
@@ -559,7 +568,8 @@ static int reftable_obj_record_decode(void *rec, struct strbuf key,
        int n = 0;
        uint64_t last;
        int j;
-       r->hash_prefix = reftable_malloc(key.len);
+
+       REFTABLE_ALLOC_ARRAY(r->hash_prefix, key.len);
        memcpy(r->hash_prefix, key.buf, key.len);
        r->hash_prefix_len = key.len;
 
@@ -577,7 +587,7 @@ static int reftable_obj_record_decode(void *rec, struct strbuf key,
        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);
@@ -625,6 +635,25 @@ static int reftable_obj_record_equal_void(const void *a, const void *b, int hash
        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,
@@ -635,6 +664,7 @@ static struct reftable_record_vtable reftable_obj_record_vtable = {
        .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,
 };
 
@@ -715,12 +745,12 @@ static void reftable_log_record_copy_from(void *rec, const void *src_rec,
                }
 
                if (dst->value.update.new_hash) {
-                       dst->value.update.new_hash = reftable_malloc(hash_size);
+                       REFTABLE_ALLOC_ARRAY(dst->value.update.new_hash, 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);
+                       REFTABLE_ALLOC_ARRAY(dst->value.update.old_hash, hash_size);
                        memcpy(dst->value.update.old_hash,
                               src->value.update.old_hash, hash_size);
                }
@@ -953,6 +983,22 @@ static int reftable_log_record_equal_void(const void *a,
                                         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)
 {
@@ -1002,6 +1048,7 @@ static struct reftable_record_vtable reftable_log_record_vtable = {
        .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,
 };
 
@@ -1077,6 +1124,13 @@ static int reftable_index_record_equal(const void *a, const void *b, int hash_si
        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;
@@ -1094,6 +1148,7 @@ static struct reftable_record_vtable reftable_index_record_vtable = {
        .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,
 };
 
@@ -1147,6 +1202,14 @@ int reftable_record_is_deletion(struct reftable_record *rec)
                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)
@@ -1257,45 +1320,22 @@ reftable_record_vtable(struct reftable_record *rec)
        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)
index fd80cd451d5d4c3ffea93d5bb1c7a0014531fae9..a05e2be17971d5e26f27871159cda293aa2640ad 100644 (file)
@@ -62,6 +62,12 @@ struct reftable_record_vtable {
        /* 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,9 +75,6 @@ struct reftable_record_vtable {
 /* 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,
@@ -100,8 +103,8 @@ struct reftable_obj_record {
 /* 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,7 +116,11 @@ struct reftable_record {
        } 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);
index 2876db7d2708aa36d861181775de74c966862c2d..a86cff552661ba0f00be32200bf8b0c800a9fa87 100644 (file)
 
 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);
@@ -231,8 +231,8 @@ static void test_reftable_log_record_roundtrip(void)
                                .value_type = REFTABLE_LOG_UPDATE,
                                .value = {
                                        .update = {
-                                               .new_hash = reftable_calloc(GIT_SHA1_RAWSZ),
-                                               .old_hash = reftable_calloc(GIT_SHA1_RAWSZ),
+                                               .new_hash = reftable_calloc(GIT_SHA1_RAWSZ, 1),
+                                               .old_hash = reftable_calloc(GIT_SHA1_RAWSZ, 1),
                                                .name = xstrdup("old name"),
                                                .email = xstrdup("old@email"),
                                                .message = xstrdup("old message"),
index 957349693247ae34c0b248a130e1e97f83ba1a71..7570e4acf9eec740057a4c6ba8a615878f48799f 100644 (file)
@@ -140,8 +140,8 @@ int validate_ref_record_addition(struct reftable_table tab,
 {
        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;
index 699e1aea4122d66a8a00451521f7d902858474c2..b9cc62554ea1569a66e8fc08be40e04452e3032c 100644 (file)
@@ -30,7 +30,7 @@ static void test_conflict(void)
        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,
index 1a6d16915ab453b480f0ca657448034a11ddf6be..c91a2d83a2b8a956e4b2d4999b4548ed1860269b 100644 (file)
@@ -33,7 +33,7 @@ struct reftable_table;
    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' */
index db8de197f6c42a0de203fc1fbca8174f7f4d2938..7c7cae5f99b7cd4c2be74707bb550366e1088a35 100644 (file)
@@ -88,6 +88,7 @@ struct reftable_stats {
 /* 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
index 7ffeb3ee1074bc59aa094fabbbce24d1f3cfaf9a..b64e55648aa8798054bb7b8a781376e7fa3bc2af 100644 (file)
@@ -8,6 +8,7 @@ https://developers.google.com/open-source/licenses/bsd
 
 #include "stack.h"
 
+#include "../write-or-die.h"
 #include "system.h"
 #include "merged.h"
 #include "reader.h"
@@ -16,7 +17,6 @@ https://developers.google.com/open-source/licenses/bsd
 #include "reftable-record.h"
 #include "reftable-merged.h"
 #include "writer.h"
-
 #include "tempfile.h"
 
 static int stack_try_add(struct reftable_stack *st,
@@ -24,7 +24,8 @@ static int stack_try_add(struct reftable_stack *st,
                                            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);
@@ -47,11 +48,17 @@ static ssize_t reftable_fd_write(void *arg, const void *data, size_t 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;
 
@@ -66,6 +73,7 @@ int reftable_new_stack(struct reftable_stack **dest, const char *dir,
        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;
 
@@ -93,7 +101,7 @@ static int fd_read_lines(int fd, char ***namesp)
                goto done;
        }
 
-       buf = reftable_malloc(size + 1);
+       REFTABLE_ALLOC_ARRAY(buf, size + 1);
        if (read_in_full(fd, buf, size) != size) {
                err = REFTABLE_IO_ERROR;
                goto done;
@@ -113,7 +121,7 @@ int read_lines(const char *filename, char ***namesp)
        int err = 0;
        if (fd < 0) {
                if (errno == ENOENT) {
-                       *namesp = reftable_calloc(sizeof(char *));
+                       REFTABLE_CALLOC_ARRAY(*namesp, 1);
                        return 0;
                }
 
@@ -175,6 +183,12 @@ void reftable_stack_destroy(struct reftable_stack *st)
                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);
@@ -184,8 +198,7 @@ void reftable_stack_destroy(struct reftable_stack *st)
 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];
@@ -196,18 +209,18 @@ static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
 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;
        struct strbuf table_path = STRBUF_INIT;
-       int i;
+       int err = 0;
+       size_t i;
 
        while (*names) {
                struct reftable_reader *rd = NULL;
@@ -215,11 +228,10 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
 
                /* 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;
                        }
                }
@@ -304,69 +316,134 @@ static int tv_cmp(struct timeval *a, struct timeval *b)
 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;
-       if (err < 0)
-               return err;
+       int tries = 0, err;
+       int fd = -1;
 
+       err = gettimeofday(&deadline, NULL);
+       if (err < 0)
+               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;
+
+               err = gettimeofday(&now, NULL);
+               if (err < 0)
+                       goto out;
 
-               /* Only look at deadlines after the first few times. This
-                  simplifies debugging in GDB */
+               /*
+                * 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;
 
-               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);
-                       break;
-               }
-               if (err != REFTABLE_NOT_EXIST_ERROR) {
-                       free_names(names);
-                       return err;
-               }
+               fd = open(st->list_file, O_RDONLY);
+               if (fd < 0) {
+                       if (errno != ENOENT) {
+                               err = REFTABLE_IO_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_CALLOC_ARRAY(names, 1);
+               } else {
+                       err = fd_read_lines(fd, &names);
+                       if (err < 0)
+                               goto out;
                }
 
+               err = reftable_stack_reload_once(st, names, reuse_open);
+               if (!err)
+                       break;
+               if (err != REFTABLE_NOT_EXIST_ERROR)
+                       goto out;
+
+               /*
+                * 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
@@ -375,8 +452,44 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 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;
 
@@ -443,7 +556,7 @@ struct reftable_addition {
        struct reftable_stack *stack;
 
        char **new_tables;
-       int new_tables_len;
+       size_t new_tables_len, new_tables_cap;
        uint64_t next_update_index;
 };
 
@@ -494,8 +607,9 @@ done:
 
 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);
@@ -505,6 +619,7 @@ static void reftable_addition_close(struct reftable_addition *add)
        reftable_free(add->new_tables);
        add->new_tables = NULL;
        add->new_tables_len = 0;
+       add->new_tables_cap = 0;
 
        delete_tempfile(&add->lock_file);
        strbuf_release(&nm);
@@ -523,8 +638,8 @@ int reftable_addition_commit(struct reftable_addition *add)
 {
        struct strbuf table_list = STRBUF_INIT;
        int lock_file_fd = get_tempfile_fd(add->lock_file);
-       int i = 0;
        int err = 0;
+       size_t i;
 
        if (add->new_tables_len == 0)
                goto done;
@@ -545,6 +660,9 @@ int reftable_addition_commit(struct reftable_addition *add)
                goto done;
        }
 
+       fsync_component_or_die(FSYNC_COMPONENT_REFERENCE, lock_file_fd,
+                              get_tempfile_path(add->lock_file));
+
        err = rename_tempfile(&add->lock_file, add->stack->list_file);
        if (err < 0) {
                err = REFTABLE_IO_ERROR;
@@ -552,14 +670,14 @@ int reftable_addition_commit(struct reftable_addition *add)
        }
 
        /* success, no more state to clean up. */
-       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;
 
@@ -576,7 +694,7 @@ int reftable_stack_new_addition(struct reftable_addition **dest,
 {
        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) {
@@ -639,7 +757,7 @@ int reftable_addition_add(struct reftable_addition *add,
                        goto done;
                }
        }
-       wr = reftable_new_writer(reftable_fd_write, &tab_fd,
+       wr = reftable_new_writer(reftable_fd_write, reftable_fd_flush, &tab_fd,
                                 &add->stack->config);
        err = write_table(wr, arg);
        if (err < 0)
@@ -684,11 +802,9 @@ int reftable_addition_add(struct reftable_addition *add,
                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);
@@ -714,7 +830,8 @@ uint64_t reftable_stack_next_update_index(struct reftable_stack *st)
        return 1;
 }
 
-static int stack_compact_locked(struct reftable_stack *st, int first, int last,
+static int stack_compact_locked(struct reftable_stack *st,
+                               size_t first, size_t last,
                                struct strbuf *temp_tab,
                                struct reftable_log_expiry_config *config)
 {
@@ -731,7 +848,13 @@ static int stack_compact_locked(struct reftable_stack *st, int first, int last,
        strbuf_addstr(temp_tab, ".temp.XXXXXX");
 
        tab_fd = mkstemp(temp_tab->buf);
-       wr = reftable_new_writer(reftable_fd_write, &tab_fd, &st->config);
+       if (st->config.default_permissions &&
+           chmod(temp_tab->buf, 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)
@@ -758,22 +881,21 @@ done:
 }
 
 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;
@@ -857,25 +979,20 @@ done:
 }
 
 /* <  0: error. 0 == OK, > 0 attempt failed; could retry. */
-static int stack_compact_range(struct reftable_stack *st, int first, int last,
+static int stack_compact_range(struct reftable_stack *st,
+                              size_t first, size_t last,
                               struct reftable_log_expiry_config *expiry)
 {
+       char **delete_on_success = NULL, **subtable_locks = NULL, **listp = NULL;
        struct strbuf temp_tab_file_name = 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;
+       size_t i, j, compact_count;
        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;
 
        if (first > last || (!expiry && first == last)) {
@@ -883,6 +1000,10 @@ static int stack_compact_range(struct reftable_stack *st, int first, int last,
                goto done;
        }
 
+       compact_count = last - first + 1;
+       REFTABLE_CALLOC_ARRAY(delete_on_success, compact_count + 1);
+       REFTABLE_CALLOC_ARRAY(subtable_locks, compact_count + 1);
+
        st->stats.attempts++;
 
        strbuf_reset(&lock_file_name);
@@ -1008,6 +1129,14 @@ static int stack_compact_range(struct reftable_stack *st, int first, int last,
                unlink(new_table_path.buf);
                goto done;
        }
+
+       err = fsync_component(FSYNC_COMPONENT_REFERENCE, lock_file_fd);
+       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) {
@@ -1040,12 +1169,14 @@ static int stack_compact_range(struct reftable_stack *st, int first, int last,
 done:
        free_names(delete_on_success);
 
-       listp = subtable_locks;
-       while (*listp) {
-               unlink(*listp);
-               listp++;
+       if (subtable_locks) {
+               listp = subtable_locks;
+               while (*listp) {
+                       unlink(*listp);
+                       listp++;
+               }
+               free_names(subtable_locks);
        }
-       free_names(subtable_locks);
        if (lock_file_fd >= 0) {
                close(lock_file_fd);
                lock_file_fd = -1;
@@ -1064,17 +1195,17 @@ done:
 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 > 0)
                st->stats.failures++;
-       }
        return err;
 }
 
@@ -1094,12 +1225,11 @@ int fastlog2(uint64_t sz)
        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;
@@ -1125,29 +1255,27 @@ struct segment *sizes_to_segments(int *seglen, uint64_t *sizes, int n)
        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;
+       struct segment *segs;
+       size_t seglen = 0, i;
+
+       segs = sizes_to_segments(&seglen, sizes, n);
        for (i = 0; i < seglen; i++) {
-               if (segment_size(&segs[i]) == 1) {
+               if (segment_size(&segs[i]) == 1)
                        continue;
-               }
 
-               if (segs[i].log < min_seg.log) {
+               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];
@@ -1160,7 +1288,7 @@ struct segment suggest_compaction_segment(uint64_t *sizes, int n)
 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;
@@ -1259,17 +1387,12 @@ static int stack_check_addition(struct reftable_stack *st,
        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;
        }
 
index f57005846e561272054883e856d7a1dd14863a19..d919455669ede0b2872a2d19ee12870479ab99ab 100644 (file)
@@ -14,7 +14,10 @@ https://developers.google.com/open-source/licenses/bsd
 #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 @@ struct reftable_stack {
 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
index 289e902146470035e4dd0109af6b8436e97d40d4..509f4866236024f5546100da7121996f1c963e08 100644 (file)
@@ -443,15 +443,16 @@ static void test_reftable_stack_add(void)
        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;
@@ -509,12 +510,32 @@ static void test_reftable_stack_add(void)
                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);
 }
 
@@ -711,7 +732,7 @@ static void test_sizes_to_segments(void)
        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);
@@ -726,7 +747,7 @@ static void test_sizes_to_segments(void)
 
 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);
@@ -735,8 +756,7 @@ static void test_sizes_to_segments_empty(void)
 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);
index 04044fc1a0fb5ffa143264527b882afb752fc2c6..4066924eee45dc6b4fe5b353f7fe54c9dc9f7d7e 100644 (file)
@@ -20,3 +20,8 @@ ssize_t strbuf_add_void(void *b, const void *data, size_t sz)
        strbuf_add(b, data, sz);
        return sz;
 }
+
+int noop_flush(void *arg)
+{
+       return 0;
+}
index ee44f735aea51d03fc3400a9340ccbd7af881f9e..687390f9c239a397652a9a4c9a0bbb7a3510d232 100644 (file)
@@ -56,4 +56,6 @@ void set_test_hash(uint8_t *p, int i);
  */
 ssize_t strbuf_add_void(void *b, const void *data, size_t sz);
 
+int noop_flush(void *);
+
 #endif
index a5bf880985472dee7cd7c9169047752245599488..528f33ae388d0a6ad93eaa5e3f604596f07071fb 100644 (file)
@@ -20,8 +20,8 @@ struct tree_node *tree_search(void *key, struct tree_node **rootp,
                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;
index ee4590e20f84dd442fde4f7506e27e568f9ad509..1d9ff0fbfabcc387e29697c0458467c882708c39 100644 (file)
@@ -49,7 +49,7 @@ static int padded_write(struct reftable_writer *w, uint8_t *data, size_t len,
 {
        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 @@ static struct strbuf reftable_empty_strbuf = STRBUF_INIT;
 
 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 @@ reftable_new_writer(ssize_t (*writer_func)(void *, const void *, size_t),
                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 @@ static void writer_index_hash(struct reftable_writer *w, struct strbuf *hash)
                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 @@ int reftable_writer_add_logs(struct reftable_writer *w,
 
 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,33 +424,26 @@ static int writer_finish_section(struct reftable_writer *w)
                                        .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 = writer_flush_block(w);
+               if (err < 0)
+                       return err;
 
-                       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++) {
+               for (i = 0; i < idx_len; i++)
                        strbuf_release(&idx[i].last_key);
-               }
                reftable_free(idx);
        }
 
-       err = writer_flush_block(w);
-       if (err < 0)
-               return err;
-
+       /*
+        * 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);
 
        bstats = writer_reftable_block_stats(w, typ);
@@ -603,6 +611,12 @@ int reftable_writer_close(struct reftable_writer *w)
        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 @@ done:
 
 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 @@ static int writer_flush_nonempty_block(struct reftable_writer *w)
        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);
index 09b88673d9757536c3c0f5122f991703eb94a06e..8d0df9cc528dba79677716b6b6760b93c7dad057 100644 (file)
@@ -16,6 +16,7 @@ https://developers.google.com/open-source/licenses/bsd
 
 struct reftable_writer {
        ssize_t (*write)(void *, const void *, size_t);
+       int (*flush)(void *);
        void *write_arg;
        int pending_padding;
        struct strbuf last_key;
index cb0182b582a5cb4d13718d4a47cfb07c078464a0..1161dc7fed689259141ae2eb5403d84b906027d3 100644 (file)
@@ -1446,8 +1446,14 @@ static int stateless_connect(const char *service_name)
         * 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);
@@ -1485,9 +1491,11 @@ static int stateless_connect(const char *service_name)
 
        /*
         * 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) {
index e07b316eac3f5242f59e8d586f8f8e52711be494..9090632e96daddfc1217bc64ae4e0dac11bdb993 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -105,7 +105,7 @@ static int remotes_hash_cmp(const void *cmp_data UNUSED,
        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);
 }
@@ -189,8 +189,7 @@ static int branches_hash_cmp(const void *cmp_data UNUSED,
        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);
 }
index 30cd4787627b7cacbc2dbceddc0babee1b72cb3e..a0b590bc6c0bb927fb0846055f4c4826e9558c80 100644 (file)
@@ -43,6 +43,7 @@ void prepare_repo_settings(struct repository *r)
        if (experimental) {
                r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING;
                r->settings.pack_use_bitmap_boundary_traversal = 1;
+               r->settings.pack_use_multi_pack_reuse = 1;
        }
        if (manyfiles) {
                r->settings.index_version = 4;
index d7d24d416ae4373c9283ec6bd5171b7cfdd1e759..7aacb51b65cca69ec6acd0c879dd0aa5b15978b3 100644 (file)
@@ -262,8 +262,6 @@ static void repo_clear_path_cache(struct repo_path_cache *cache)
        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);
 }
index f5269b3730c602a79adbde479f4622f124156b60..9bf1e33d2591bbd8d62c971dbc70790d250b021f 100644 (file)
@@ -24,8 +24,9 @@ enum fetch_negotiation_setting {
        FETCH_NEGOTIATION_NOOP,
 };
 
-#define REF_STORAGE_FORMAT_UNKNOWN 0
-#define REF_STORAGE_FORMAT_FILES   1
+#define REF_STORAGE_FORMAT_UNKNOWN  0
+#define REF_STORAGE_FORMAT_FILES    1
+#define REF_STORAGE_FORMAT_REFTABLE 2
 
 struct repo_settings {
        int initialized;
@@ -39,6 +40,7 @@ struct repo_settings {
        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
@@ -67,8 +69,6 @@ struct repo_path_cache {
        char *merge_rr;
        char *merge_mode;
        char *merge_head;
-       char *merge_autostash;
-       char *auto_merge;
        char *fetch_head;
        char *shallow;
 };
index ca7e77ba68c1ad4aa49f032906ba419098be0d04..13c94ded03706dc3831b0948a1fa7438fa45ab9b 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -973,6 +973,9 @@ static int handle_cache(struct index_state *istate,
                        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;
                }
        }
diff --git a/reset.c b/reset.c
index 0f2ff0fe31531fc5602025cfcd866a9348cfcf63..d619cb7115323febadbf4ae84e6764200f962cab 100644 (file)
--- a/reset.c
+++ b/reset.c
@@ -157,6 +157,11 @@ int reset_head(struct repository *r, const struct reset_head_opts *opts)
        }
 
        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) {
index d2ebdd045a4cb3fa7f298364db87cd9ae16507d1..0b18b3aa5fa5609ed073aa9d7fcaff80b5e0a206 100644 (file)
@@ -385,9 +385,13 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
 
        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;
@@ -415,15 +419,21 @@ static struct commit *handle_commit(struct rev_info *revs,
         */
        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;
@@ -1686,9 +1696,7 @@ static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
        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;
@@ -1947,6 +1955,7 @@ void repo_init_revisions(struct repository *r,
        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,
@@ -2197,13 +2206,18 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl
        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);
@@ -3849,8 +3863,6 @@ int prepare_revision_walk(struct rev_info *revs)
                                       FOR_EACH_OBJECT_PROMISOR_ONLY);
        }
 
-       oidset_init(&revs->missing_commits, 0);
-
        if (!revs->reflog_info)
                prepare_to_use_bloom_filter(revs);
        if (!revs->unsorted_input)
index 3cc88d8a8004d91eb2a404214f4ac136a845d61f..35dd7e561a60c56d2639068a6afde50099497d65 100644 (file)
@@ -474,7 +474,7 @@ static void print_advice(struct repository *r, int show_hint,
                 * 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;
        }
 
@@ -707,6 +707,8 @@ static int do_recursive_merge(struct repository *r,
        o.show_rename_progress = 1;
 
        head_tree = parse_tree_indirect(head);
+       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);
 
@@ -1667,7 +1669,7 @@ static int do_commit(struct repository *r,
                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,
@@ -2406,9 +2408,10 @@ static int do_pick_commit(struct repository *r,
        } 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);
@@ -2802,7 +2805,7 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose)
 
        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;
@@ -2811,14 +2814,15 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose)
 
        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;
@@ -3639,6 +3643,7 @@ static int do_exec(struct repository *r, const char *command_line)
        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 */
@@ -3879,6 +3884,8 @@ static int do_reset(struct repository *r,
        }
 
        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)
@@ -4116,7 +4123,7 @@ static int do_merge(struct repository *r,
 
                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);
@@ -4461,12 +4468,17 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
        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)
@@ -4493,10 +4505,16 @@ void create_autostash(struct repository *r, const char *path)
                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"));
@@ -4507,6 +4525,16 @@ void create_autostash(struct repository *r, const char *path)
        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;
@@ -4584,6 +4612,41 @@ int apply_autostash_oid(const char *stash_oid)
        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)
@@ -4766,8 +4829,10 @@ static int pick_commits(struct repository *r,
                        }
                        unlink(rebase_path_author_script());
                        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)
@@ -5108,7 +5173,7 @@ static int commit_staged_changes(struct repository *r,
                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'"),
@@ -5122,7 +5187,8 @@ static int commit_staged_changes(struct repository *r,
                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());
index 913a0f652d9ab356bc066f162fa1e5197f6900eb..dcef7bb99c08b0f8e5a29905f8f3a4d6d2d34a45 100644 (file)
@@ -225,9 +225,12 @@ void commit_post_rewrite(struct repository *r,
                         const struct object_id *new_head);
 
 void create_autostash(struct repository *r, const char *path);
+void create_autostash_ref(struct repository *r, const char *refname);
 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)
diff --git a/setup.c b/setup.c
index b38702718fbc8db7bb1ba718ecd69b7c99da80e4..b69b1cbc2adb41aa5f4df8b1aff88761ea2cba5c 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -1371,7 +1371,8 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
 
                if (is_git_directory(dir->buf)) {
                        trace2_data_string("setup", NULL, "implicit-bare-repository", dir->buf);
-                       if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT)
+                       if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT &&
+                           !ends_with_path_components(dir->buf, ".git"))
                                return GIT_DIR_DISALLOWED_BARE;
                        if (!ensure_valid_ownership(NULL, NULL, dir->buf, report))
                                return GIT_DIR_INVALID_OWNERSHIP;
@@ -1926,23 +1927,8 @@ void create_reference_database(unsigned int ref_storage_format,
        struct strbuf err = STRBUF_INIT;
        int reinit = is_reinit();
 
-       /*
-        * 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.
-        */
-       safe_create_dir(git_path("refs"), 1);
-       adjust_shared_perm(git_path("refs"));
-
        repo_set_ref_storage_format(the_repository, ref_storage_format);
-       if (refs_init_db(&err))
+       if (refs_init_db(get_main_ref_store(the_repository), 0, &err))
                die("failed to set up refs db: %s", err.buf);
 
        /*
index aeb80fc4d5a33e1b5c6f2e1bbdf4cc3279802eef..29bebd30d8acbce9f50661cef48ecdbae1e41f5a 100644 (file)
@@ -108,3 +108,11 @@ endif
 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
index 3578feb28376e308ebb1ab1adc069c6860bbe72b..e48e40cae71f975f98f1874b90fc8889c0c710e2 100644 (file)
@@ -579,8 +579,9 @@ void expand_to_path(struct index_state *istate,
                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
@@ -593,7 +594,6 @@ void expand_to_path(struct index_state *istate,
                }
 
                *replace = temp;
-               substr_len = replace - path_mutable.buf;
        }
 
 cleanup:
index 9f55c8766ba9de77437275b8796f8e348fe35e03..4715d3e51f873e7b6618a80ebc79f2d2b4ebaab7 100644 (file)
--- 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 @@ extern const char *empty_strvec[];
 
 /**
  * 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_split(struct strvec *, const char *);
 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
index f4dd482abc95e7c1668623be7c24ec163745e189..54130f6a38572b613d4b7ee8ae1cf3bc6035055d 100644 (file)
@@ -14,6 +14,8 @@
 #include "parse-options.h"
 #include "thread-utils.h"
 #include "tree-walk.h"
+#include "url.h"
+#include "urlmatch.h"
 
 /*
  * submodule cache lookup structure
@@ -228,6 +230,144 @@ in_component:
        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)
 {
index 958f320ac6ce08a0b0ac856415a173d5fa7c2600..b6133af71b00d61abdafc3aa736fe4f4018695cf 100644 (file)
@@ -89,6 +89,9 @@ int config_set_in_gitmodules_file_gently(const char *key, const char *value);
  */
 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
index 213da79f66116f3db835036140affb98406a934a..40f13a36857179c39d31891396c892c5b7e54f72 100644 (file)
@@ -1687,8 +1687,6 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
                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);
@@ -1698,15 +1696,11 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
                        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;
        }
@@ -1714,12 +1708,8 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
        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;
@@ -1728,8 +1718,8 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
                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");
@@ -1737,7 +1727,6 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err,
                                          append_oid_to_argv, &cp->args);
 
                *task_cb = task;
-               strbuf_release(&submodule_prefix);
                return 1;
        }
 
index b7a6fefe28d614830a63b0b13e6c597cfd6f9f52..2d95046f26ec6ebad9d8707e8c002aadc665a282 100644 (file)
@@ -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 @@ include ../shared.mak
 # Copyright (c) 2005 Junio C Hamano
 #
 
+-include ../config.mak.uname
 -include ../config.mak.autogen
 -include ../config.mak
 
@@ -42,14 +46,16 @@ TPERF = $(sort $(wildcard perf/p[0-9][0-9][0-9][0-9]-*.sh))
 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_TESTS = $(sort $(filter-out %.pdb unit-tests/bin/t-basic%,$(wildcard unit-tests/bin/t-*)))
+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 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
diff --git a/t/helper/test-ctype.c b/t/helper/test-ctype.c
deleted file mode 100644 (file)
index e5659df..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "test-tool.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);    \
-       }                                       \
-       if (t(EOF))                             \
-               report_error(#t, EOF);          \
-}
-
-#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 UNUSED, const char **argv UNUSED)
-{
-       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-prio-queue.c b/t/helper/test-prio-queue.c
deleted file mode 100644 (file)
index f0bf255..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "test-tool.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;
-}
-
-static void show(int *v)
-{
-       if (!v)
-               printf("NULL\n");
-       else
-               printf("%d\n", *v);
-       free(v);
-}
-
-int cmd__prio_queue(int argc UNUSED, 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;
-}
index 702ec1f128ad42443bc093614dee971c7106af77..7a0f6cac53d2433ade595ce95ab2d30e214b2ede 100644 (file)
@@ -221,15 +221,21 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv)
        return ret;
 }
 
+static int each_reflog(const char *refname, void *cb_data UNUSED)
+{
+       printf("%s\n", refname);
+       return 0;
+}
+
 static int cmd_for_each_reflog(struct ref_store *refs,
                               const char **argv UNUSED)
 {
-       return refs_for_each_reflog(refs, each_ref, NULL);
+       return refs_for_each_reflog(refs, each_reflog, NULL);
 }
 
-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 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,
@@ -241,14 +247,14 @@ static int cmd_for_each_reflog_ent(struct ref_store *refs, const char **argv)
 {
        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)
index 50c154d0370bfbdd91b70747133977a752f2b504..7197969a08149699a14c758ceef1223e494b7c35 100644 (file)
@@ -9,12 +9,19 @@
 #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[] = {
@@ -31,31 +38,26 @@ static const char *submodule_resolve_relative_url_usage[] = {
 
 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;
 }
 
@@ -69,7 +71,20 @@ static int cmd__submodule_check_name(int argc, const char **argv)
        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)
@@ -195,6 +210,7 @@ static int cmd__submodule_config_writeable(int argc, const char **argv UNUSED)
 
 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 },
index 37ba996539b72f66316722cc73094d44aef6e61c..482a1e58a4b6ec922f1d5abd39848acda79a78bf 100644 (file)
@@ -19,7 +19,6 @@ static struct test_cmd cmds[] = {
        { "config", cmd__config },
        { "crontab", cmd__crontab },
        { "csprng", cmd__csprng },
-       { "ctype", cmd__ctype },
        { "date", cmd__date },
        { "delta", cmd__delta },
        { "dir-iterator", cmd__dir_iterator },
@@ -56,7 +55,6 @@ static struct test_cmd cmds[] = {
        { "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 },
index 8a1a7c63daa4dabf1b92d35420f2b2f5c36b136c..b1be7cfcf593d0d34ac5f851738ae2fbdf93478f 100644 (file)
@@ -12,7 +12,6 @@ int cmd__chmtime(int argc, const char **argv);
 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__dir_iterator(int argc, const char **argv);
@@ -49,7 +48,6 @@ int cmd__partial_clone(int argc, const char **argv);
 int cmd__path_utils(int argc, const char **argv);
 int cmd__pcre2_config(int argc, const char **argv);
 int cmd__pkt_line(int argc, const char **argv);
-int cmd__prio_queue(int argc, const char **argv);
 int cmd__proc_receive(int argc, const char **argv);
 int cmd__progress(int argc, const char **argv);
 int cmd__reach(int argc, const char **argv);
index 15fc9a31e2cc03689e76876693e39367626784ff..44799c0d38fdb35d1c56a3e9ac8d91a963d2ee1c 100644 (file)
@@ -50,6 +50,7 @@ helper_test_clean() {
        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
diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh
deleted file mode 100755 (executable)
index eea9910..0000000
+++ /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
index c13057a4ca3be22e431771fd60d5ab0325349bdc..0dcfb760a2ba922570d12e17cc1ff6297bdab913 100755 (executable)
@@ -17,7 +17,6 @@ test_expect_success 'advice should be printed when config variable is unset' '
 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 &&
index a34de5642073d1323b37b7c82f56edfe798fcfdd..a7f4de4a43ffa022ae56ebf14999cbe81900a4a9 100755 (executable)
@@ -9,7 +9,7 @@ test_expect_success setup '
 
        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 setup '
 
 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 @@ test_expect_success UNZIP 'zip archive' '
 
        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
 
index 038b8b788d7dea688c2efa807e1d1856b8f33c13..804885637954a57ba85e139591e6a0f5a5167034 100755 (executable)
@@ -78,4 +78,12 @@ 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_done
index f18f9284a5bf3961a3f5373f887b808e55cd9b75..0ecec2ba71116959fd2efceac7fc46683d1a3e91 100755 (executable)
@@ -9,10 +9,6 @@ Verify wrappers and compatibility functions.
 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
index 961b54b06cf875f222ff6281a65e84b895c159cb..6657c114a36c7ea650a974a2f556b7f87435c251 100755 (executable)
@@ -2,6 +2,7 @@
 
 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' '
index 8798feefe3f6700ee90d3854299802a60536df7d..fca39048fe8840099c2d7a393e91dfd3a6683029 100755 (executable)
@@ -39,9 +39,9 @@ test_expect_success 'sanity check "System Info" section' '
 
        sed -ne "/^\[System Info\]$/,/^$/p" <git-bugreport-format.txt >system &&
 
-       # The beginning should match "git version --build-info" verbatim,
+       # The beginning should match "git version --build-options" verbatim,
        # but rather than checking bit-for-bit equality, just test some basics.
-       grep "git version [0-9]." system &&
+       grep "git version " system &&
        grep "shell-path: ." system &&
 
        # After the version, there should be some more info.
index 095574bfc6edf2aaf835b2ff43bb8cd35f792591..72ae405c3ed979edee54cbe83e73ec566d23f6d1 100755 (executable)
@@ -32,9 +32,24 @@ commands.
 . ./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" ||
@@ -59,4 +74,11 @@ fi
 # 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
index 6b6424b3df17135666b332b7857ec8382c813ece..88a66f09040ce0aa2a3a653579d6c3685e750ba1 100755 (executable)
@@ -49,7 +49,7 @@ test_expect_success 'convert shallow clone to partial clone' '
        test_cmp_config -C client 1 core.repositoryformatversion
 '
 
-test_expect_success SHA1,REFFILES '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 @@ test_expect_success SHA1,REFFILES 'convert to partial clone with noop extension'
        git -C client fetch --unshallow --filter="blob:none"
 '
 
-test_expect_success SHA1,REFFILES '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 @@ test_expect_success 'lazy-fetch when accessing object not in the_repository' '
        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/t0600-reffiles-backend.sh b/t/t0600-reffiles-backend.sh
new file mode 100755 (executable)
index 0000000..6421434
--- /dev/null
@@ -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
similarity index 81%
rename from t/t3210-pack-refs.sh
rename to t/t0601-reffiles-pack-refs.sh
index 7f4e98db7db24363a451fa4b1ae8412b6e1277c3..c309d2bae8a19816907b81d82cef9099b2fa21e9 100755 (executable)
@@ -15,6 +15,12 @@ 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
 '
@@ -26,6 +32,14 @@ test_expect_success 'prepare a trivial repository' '
        HEAD=$(git rev-parse --verify HEAD)
 '
 
+test_expect_success 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '
+       N=`find .git/refs -type f | wc -l` &&
+       test "$N" != 0 &&
+       test-tool ref-store main pack-refs PACK_REFS_PRUNE,PACK_REFS_ALL &&
+       N=`find .git/refs -type f` &&
+       test -z "$N"
+'
+
 SHA1=
 
 test_expect_success 'see if git show-ref works as expected' '
@@ -294,4 +308,54 @@ test_expect_success SYMLINKS 'pack symlinked 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_done
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
new file mode 100755 (executable)
index 0000000..6a131e4
--- /dev/null
@@ -0,0 +1,887 @@
+#!/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
+}
+
+for umask in 002 022
+do
+       test_expect_success POSIXPERM 'init: honors core.sharedRepository' '
+               test_when_finished "rm -rf repo" &&
+               (
+                       umask $umask &&
+                       git init --shared=true repo &&
+                       test 1 = "$(git -C repo config core.sharedrepository)"
+               ) &&
+               test_expect_perms "-rw-rw-r--" repo/.git/reftable/tables.list &&
+               for table in repo/.git/reftable/*.ref
+               do
+                       test_expect_perms "-rw-rw-r--" "$table" ||
+                       return 1
+               done
+       '
+done
+
+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 '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: 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
+'
+
+for umask in 002 022
+do
+       test_expect_success POSIXPERM 'pack-refs: honors core.sharedRepository' '
+               test_when_finished "rm -rf repo" &&
+               (
+                       umask $umask &&
+                       git init --shared=true repo &&
+                       test_commit -C repo A &&
+                       test_line_count = 3 repo/.git/reftable/tables.list
+               ) &&
+               git -C repo pack-refs &&
+               test_expect_perms "-rw-rw-r--" repo/.git/reftable/tables.list &&
+               for table in repo/.git/reftable/*.ref
+               do
+                       test_expect_perms "-rw-rw-r--" "$table" ||
+                       return 1
+               done
+       '
+done
+
+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 (executable)
index 0000000..5e05b9c
--- /dev/null
@@ -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
index f4e27521344920ca5cf85b20a263487e2d794b52..31c38786870849e7a815f32a08933f059c9c8ffb 100755 (executable)
@@ -1098,15 +1098,20 @@ test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
        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 &&
@@ -1157,10 +1162,16 @@ test_expect_success 'git -c works with aliases of builtins' '
 '
 
 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' '
@@ -2009,11 +2020,11 @@ test_expect_success '--show-origin getting a single key' '
 '
 
 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' '
@@ -2052,22 +2063,33 @@ test_expect_success '--show-origin stdin with file include' '
 '
 
 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' '
index e5a0d65caa3e4cc363319826f37a4254bfc99b33..b1eb5c01b899daf9a0075393e3e79d47da069d5d 100755 (executable)
@@ -137,22 +137,6 @@ test_expect_success POSIXPERM 'info/refs respects umask in unshared repo' '
        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 &&
index 179474fa651e159bf68714620ab4f1be2c259fe5..42caa0d2978b58755d43b3361589c6e7691c34db 100755 (executable)
@@ -9,10 +9,6 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./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 'setup' '
 '
 
 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 &&
@@ -79,8 +80,13 @@ mkconfig () {
 
 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 @@ allow 1 noop-v1
 EOF
 
 test_expect_success 'precious-objects allowed' '
-       mkconfig 1 preciousObjects >.git/config &&
+       git config core.repositoryFormatVersion 1 &&
+       git config extensions.preciousObjects 1 &&
        check_allow
 '
 
index f18843bf7aa293d26ef1aabc7fbb8f2c1a8dca03..6ebc3ef9453b71dbc7d90a052834af482dfd48ec 100755 (executable)
@@ -288,33 +288,6 @@ test_expect_success "set $m (logged by touch)" '
        test $A = $(git show-ref -s --verify $m)
 '
 
-test_expect_success REFFILES '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 REFFILES '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
@@ -453,15 +426,15 @@ test_expect_success 'Query "main@{2005-05-28}" (past end of history)' '
 rm -f expect
 git update-ref -d $m
 
-test_expect_success REFFILES 'query reflog with gap' '
+test_expect_success 'query reflog with gap' '
        test_when_finished "git update-ref -d $m" &&
 
-       git update-ref $m $F &&
-       cat >.git/logs/$m <<-EOF &&
-       $Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
-       $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
-       $D $F $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
-       EOF
+       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 &&
@@ -524,51 +497,51 @@ test_expect_success 'given old value for missing pseudoref, do not create' '
 
 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 $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 $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 $A = $(git show-ref -s --verify PSEUDOREF) &&
        test_grep "already exists" err
 '
 
@@ -1668,13 +1641,4 @@ test_expect_success PIPE 'transaction flushes status updates' '
        test_cmp expected actual
 '
 
-test_expect_success REFFILES '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
index 3241d3591799415e423e4f6155e4111de778a964..5c60d6f812dc20c1c86f5125b5feabb4ce1df9ce 100755 (executable)
@@ -106,9 +106,8 @@ test_expect_success LONG_REF 'we can parse long symbolic ref' '
 '
 
 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' '
index ec1957b709e0863f62912caafe96ebddf6a53d5b..33fb7a38ffff849db2a825de61eacbc34a8eb11d 100755 (executable)
@@ -174,6 +174,14 @@ test_expect_success 'show-ref --verify HEAD' '
        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/&/#"
@@ -262,9 +270,9 @@ test_expect_success '--exists with non-commit object' '
 
 test_expect_success '--exists with directory fails with generic error' '
        cat >expect <<-EOF &&
-       error: failed to look up reference: Is a directory
+       error: reference does not exist
        EOF
-       test_expect_code 1 git show-ref --exists refs/heads 2>err &&
+       test_expect_code 2 git show-ref --exists refs/heads 2>err &&
        test_cmp expect err
 '
 
index 0369beea33b1aff41662390245fdf2ccf21b1cfb..98e9158bd2ab41f17af11612ef1bfd5e65796605 100755 (executable)
@@ -92,9 +92,6 @@ df_test() {
        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 @@ df_test() {
                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 'one new ref is a simple prefix of another' '
 
 '
 
-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,169 +393,4 @@ test_expect_success 'incorrect old value blocks indirect no-deref delete' '
        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 &&
-       # 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_grep "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
index e4627cf1b61f0b3128a79e28f15a9d5e5693e30c..a6bcd62ab658eefcd11ae1778692ad6d7f4c3008 100755 (executable)
@@ -15,14 +15,6 @@ test_expect_success 'setup' '
        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_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
        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 'verify_ref(new-main)' '
 '
 
 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_expect_success 'delete_reflog(HEAD)' '
        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
 '
index e6a7f7334b6a96e4aa310532c8dc7d6c727fdd16..c01f0f14a1266f93f9066c738693d7c8a9a526dc 100755 (executable)
@@ -63,11 +63,11 @@ test_expect_success 'verify_ref(new-main)' '
 '
 
 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
 '
index 05b1881c5911780b53a3d882dbe1a4dfae0034d8..48b1c92a41450b25645d6cd782aa86c8e630164b 100755 (executable)
@@ -53,41 +53,4 @@ test_expect_success 'create_symref(FOO, refs/heads/main)' '
        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
index f23c0152a858265e4a8c6b0c068e00823aadb444..7748973733e6adab07117192accabf7698189681 100755 (executable)
@@ -5,6 +5,12 @@ test_description='avoid rewriting packed-refs unnecessarily'
 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.
index a0ff8d51f04b2d9ad34060a4f42290577d6673f7..5bf883f1e363306a74b15df1ab02988564aa3208 100755 (executable)
@@ -354,36 +354,6 @@ test_expect_success 'stale dirs do not cause d/f conflicts (reflogs off)' '
        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_expect_failure 'reflog with non-commit entries displays all entries' '
        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 &&
        (
@@ -478,4 +436,112 @@ test_expect_success 'empty reflog' '
        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
index ea64cecf47bf14277c79cc54e954c64022e0519a..be6c3f472c176525b64383b726ad7b05b664accd 100755 (executable)
@@ -121,13 +121,12 @@ test_expect_success 'min/max age uses entry date to limit' '
 
 # 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
 '
index 3b531842dd4c864b45bfd89c36e14f9327d6b29e..eb4eec8becbfa64efcde4e866334363a866c01a2 100755 (executable)
@@ -17,17 +17,6 @@ test_expect_success 'setup' '
        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 ) &&
index 5d8c86b657399e0d8852909a33491449f1eaefa9..13595744190b54e539a581f2409ab3be5e3932d3 100755 (executable)
@@ -8,6 +8,12 @@ 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
index bc136833c1098d1c4e43d4b05cfadc7bef50b24c..79df65ec7f619baa053b2aa50d5ec08557cb820a 100755 (executable)
@@ -144,11 +144,6 @@ test_expect_success 'main@{n} for various n' '
        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 &&
index 3c8135831b827d592c67c8d1316b1d768e87dc26..04f53b1ea14cb5046ee3cf3ad70804812c6fce8a 100755 (executable)
@@ -29,36 +29,33 @@ test_expect_success REFFILES 'checkout notices failure to lock HEAD' '
        test_must_fail git checkout -b other
 '
 
-test_expect_success REFFILES 'create ref directory/file conflict scenario' '
+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
        }
 '
 
-test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to branch)' '
+test_expect_success 'checkout away from d/f HEAD (unpacked, to branch)' '
        reset_to_df &&
        git checkout main
 '
 
-test_expect_success REFFILES 'checkout away from d/f HEAD (unpacked, to detached)' '
+test_expect_success 'checkout away from d/f HEAD (unpacked, to detached)' '
        reset_to_df &&
        git checkout --detach main
 '
 
-test_expect_success REFFILES 'pack refs' '
+test_expect_success 'pack refs' '
        git pack-refs --all --prune
 '
 
-test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to branch)' '
+test_expect_success 'checkout away from d/f HEAD (packed, to branch)' '
        reset_to_df &&
        git checkout main
 '
 
-test_expect_success REFFILES 'checkout away from d/f HEAD (packed, to detached)' '
+test_expect_success 'checkout away from d/f HEAD (packed, to detached)' '
        reset_to_df &&
        git checkout --detach main
 '
index 747eb5563eca8f1df4374f179a08019221ba1fac..c4f9bf09aa4fd8d541a858d55cfbff34e78de1c0 100755 (executable)
@@ -38,26 +38,32 @@ test_expect_success 'git checkout -p with staged changes' '
        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
-'
-
-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 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
-'
+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 $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 $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
index 947d1587ac8be9a2ab333049a2196c4e211185e6..a5c7358eeabefb3dacc94ae94e2c61d200381ac5 100755 (executable)
@@ -86,7 +86,7 @@ test_expect_success '--orphan makes reflog by default' '
        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 &&
index 8202ef8c74f639fb53a16b943fb5923ecd4e420e..bce284c2978848967ffe88764e396fad5059df54 100755 (executable)
@@ -45,6 +45,18 @@ test_expect_success 'checkout branch does not detach' '
        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 &&
index a97416ce65474df17e4133c8e14b23736ab7fc8d..a3b1449ef1166756e5bd029a2f94ac6e1c50b03b 100755 (executable)
@@ -113,7 +113,7 @@ test_expect_success 'checkout of branch from multiple remotes fails with advice'
        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 &&
 
index b5c5c0ff7e37ced9b6b50a06e4ec3e30c536cd53..27e85be40ad6957f9070a609d83ae334776ed855 100755 (executable)
@@ -4,7 +4,7 @@ test_description='git restore --patch'
 
 . ./lib-patch-mode.sh
 
-test_expect_success PERL 'setup' '
+test_expect_success 'setup' '
        mkdir dir &&
        echo parent >dir/foo &&
        echo dummy >bar &&
@@ -16,43 +16,47 @@ test_expect_success PERL 'setup' '
        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
-'
-
-test_expect_success PERL 'git restore -p --source=HEAD^' '
+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 '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 +64,7 @@ test_expect_success PERL 'git restore -p --source=HEAD^' '
        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 +72,7 @@ test_expect_success PERL 'git restore -p --source=HEAD^...' '
        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 +85,21 @@ test_expect_success PERL 'git restore -p handles deletion' '
 # 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 +107,7 @@ test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
        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 +115,7 @@ test_expect_success PERL 'path limiting works: foo inside dir' '
        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
 '
 
index 3742971105c15268a3c8c41642ff98a75be4f1bf..c28c04133c8a1c953406c5bdea0e83c8cb82870f 100755 (executable)
@@ -490,7 +490,8 @@ test_expect_success 'put a worktree under rebase' '
                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
        )
 '
 
@@ -531,7 +532,8 @@ test_expect_success 'checkout a branch under bisect' '
                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
        )
index de7d3014e4fb914012db343d9efd85a2818677b4..e36f4d15f2ddd4f64d6aaf8e087370c3b9f92ce5 100755 (executable)
@@ -836,35 +836,6 @@ test_expect_success 'renaming a symref is not allowed' '
        test_ref_missing refs/heads/new-topic
 '
 
-test_expect_success SYMLINKS,REFFILES '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,REFFILES '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 'test tracking setup via --track' '
        git config remote.local.url . &&
        git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
index 6a98b2df7611ed741be197640f20e1147794aa8d..a1139f79e2ccfdff2b562571bdd8bdf8aa974883 100755 (executable)
@@ -4,9 +4,6 @@ test_description='test show-branch'
 
 . ./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 &&
@@ -187,18 +184,6 @@ test_expect_success 'show branch --merge-base with N arguments' '
        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
@@ -264,4 +249,38 @@ test_expect_success 'error descriptions on orphan branch' '
        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
index 57f13929260e70865fbb8f31e89ba7ff197afeaa..e1c8c5f70110aacfaa778194093f2a79251e055c 100755 (executable)
@@ -424,16 +424,6 @@ test_expect_success 'refuse to switch to branch checked out elsewhere' '
        test_grep "already used by worktree at" err
 '
 
-test_expect_success REFFILES,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 'rebase when inside worktree subdirectory' '
        git init main-wt &&
        (
index 64b641002e4fdb049d7f958cdc54335f9c0f34ce..d1bead61fad03d8a847d75062ede8ad852f7d8e6 100755 (executable)
@@ -153,6 +153,18 @@ test_expect_success 'rebase -i with the exec command checks tree cleanness' '
        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 &&
index ebbaed147a6ce2156472f45d88a0d300ec6612ac..9f49c4228b629589d7bfcf3435fda8e7c4b308be 100755 (executable)
@@ -40,9 +40,24 @@ testrebase() {
                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 &&
index 693934ee8be9600e6edda9e691b5415f8c153b34..1a820f148155cbc65e2e1ad07275e25510e083b9 100755 (executable)
@@ -333,4 +333,14 @@ test_expect_success 'never change active branch' '
        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
index 34faeac3f1cde42633fc5a8b8b67cbf14d57edaf..00db82fb2455b8397de6ae0e5ea8e4fd96ed3d6c 100755 (executable)
@@ -200,7 +200,7 @@ test_expect_success 'drop stash reflog updates refs/stash' '
        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 @@ test_expect_success REFFILES 'drop stash reflog updates refs/stash with rewrite'
        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
 '
@@ -1516,4 +1516,56 @@ test_expect_success 'restore untracked files even when we hit conflicts' '
        )
 '
 
+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
index accfe3845c418ee90d8c9b1ec3100fa3e964d8d4..368fc2a6cc16755e4e2c6e9a7fba0e88bba5a033 100755 (executable)
@@ -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 &&
index 85be1367de6a3f73e1162ffc03563ec7b9a9eb5a..49c042a38ae987fa95f201bd3a00f55e4adeffab 100755 (executable)
@@ -286,4 +286,28 @@ test_expect_success 'basename similarity vs best similarity' '
        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
index cb094241ec899f53a4ec65f37f0c4227281722d7..1e3b2dbea48488ecb3a68a8dba82d57b6fbca2a7 100755 (executable)
@@ -663,4 +663,10 @@ test_expect_success 'diff --default-prefix overrides diff.mnemonicprefix' '
        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
index bf33aedf4b22868fd820330a460965d27e66cb91..8ebfa3c1be2fec46558c739fd3ccda37a952c225 100755 (executable)
@@ -118,4 +118,26 @@ test_expect_success 'log notes cache and still use cache for -p' '
        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
index 5ce345d309eb70425b9c04cdbfa57d1611675af8..651ec776606bb0990edc40bebf13ccc4937bb0ce 100755 (executable)
@@ -205,6 +205,18 @@ test_expect_success POSIXPERM,SYMLINKS 'diff --no-index normalizes: mode not lik
        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 e7a7295f1b687d1ed916e80e431e78c4c67657b7..4eb84440298552ec53520307641419a7f6d7fd77 100755 (executable)
@@ -41,7 +41,8 @@ test_expect_success FILEMODE 'same mode (index only)' '
        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,7 +61,8 @@ test_expect_success FILEMODE 'mode update (with index)' '
 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' '
@@ -101,4 +103,31 @@ test_expect_success POSIXPERM 'do not use core.sharedRepository for working tree
        )
 '
 
+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
index ddd205f98abf7f8d58555761eaf20006bb8433e8..60fe60d7610e2a8141c58385b2319c7fa43a5de3 100755 (executable)
@@ -2255,23 +2255,6 @@ test_expect_success 'log on empty repo fails' '
        test_grep 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_grep broken stderr
-'
-
-test_expect_success REFFILES '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 'log does not default to HEAD when rev input is given' '
        git log --branches=does-not-exist >actual &&
        test_must_be_empty actual
index 12ac43687366d72f2fa2870c72a9d5563a05fc66..d83c1d172dd2626578bb9b0918f6856f91d17ed4 100755 (executable)
@@ -945,4 +945,37 @@ test_expect_success 'check the input format when --stdin is passed' '
        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_done
index fc499cdff01d01a6079221e78b50a7924e5ecb71..961c6aac2561354f6ad57270c1362fe73c850924 100755 (executable)
@@ -239,4 +239,38 @@ check_zip with_untracked2
 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
index d402ec18b795c7ab5bc1b53d5eaaa4467e37fdbb..a58f91035d124d6710438c5524c91bdbf66b4e84 100755 (executable)
@@ -441,6 +441,47 @@ test_expect_success 'index-pack with --strict' '
        )
 '
 
+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_expect_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) &&
index 230cb3871223e1f28482c841093cb7a0e6873754..d8d2e304687b2a9e6d9313d0daa8a77848410f41 100755 (executable)
@@ -111,30 +111,4 @@ test_expect_success 'pack-refs does not silently delete broken loose ref' '
        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
index 2ba788b0421b6a7dcaf8f8ff420f5ec97cdab72b..3c20738bcebd82c2f9f8fa0d728c39bfe6ac525e 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='pack-objects multi-pack reuse'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-bitmap.sh
 
@@ -23,6 +24,27 @@ pack_position () {
        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 &&
 
@@ -34,12 +56,24 @@ test_expect_success 'preferred pack is reused for single-pack reuse' '
 
        git multi-pack-index write --bitmap &&
 
-       : >trace2.txt &&
-       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
-               git pack-objects --stdout --revs --all >/dev/null &&
+       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_reused 3 <trace2.txt &&
-       test_packs_reused 1 <trace2.txt
+       test_pack_objects_reused_all 3 1
 '
 
 test_expect_success 'enable multi-pack reuse' '
@@ -57,21 +91,11 @@ test_expect_success 'reuse all objects from subset of bitmapped packs' '
        ^$(git rev-parse A)
        EOF
 
-       : >trace2.txt &&
-       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
-               git pack-objects --stdout --revs <in >/dev/null &&
-
-       test_pack_reused 6 <trace2.txt &&
-       test_packs_reused 2 <trace2.txt
+       test_pack_objects_reused 6 2 <in
 '
 
 test_expect_success 'reuse all objects from all packs' '
-       : >trace2.txt &&
-       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
-               git pack-objects --stdout --revs --all >/dev/null &&
-
-       test_pack_reused 9 <trace2.txt &&
-       test_packs_reused 3 <trace2.txt
+       test_pack_objects_reused_all 9 3
 '
 
 test_expect_success 'reuse objects from first pack with middle gap' '
@@ -104,12 +128,7 @@ test_expect_success 'reuse objects from first pack with middle gap' '
        ^$(git rev-parse D)
        EOF
 
-       : >trace2.txt &&
-       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
-               git pack-objects --stdout --delta-base-offset --revs <in >/dev/null &&
-
-       test_pack_reused 3 <trace2.txt &&
-       test_packs_reused 1 <trace2.txt
+       test_pack_objects_reused 3 1 <in
 '
 
 test_expect_success 'reuse objects from middle pack with middle gap' '
@@ -125,12 +144,7 @@ test_expect_success 'reuse objects from middle pack with middle gap' '
        ^$(git rev-parse D)
        EOF
 
-       : >trace2.txt &&
-       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
-               git pack-objects --stdout --delta-base-offset --revs <in >/dev/null &&
-
-       test_pack_reused 3 <trace2.txt &&
-       test_packs_reused 1 <trace2.txt
+       test_pack_objects_reused 3 1 <in
 '
 
 test_expect_success 'omit delta with uninteresting base (same pack)' '
@@ -160,10 +174,6 @@ test_expect_success 'omit delta with uninteresting base (same pack)' '
        ^$base
        EOF
 
-       : >trace2.txt &&
-       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
-               git pack-objects --stdout --delta-base-offset --revs <in >/dev/null &&
-
        # We can only reuse the 3 objects corresponding to "other" from
        # the latest pack.
        #
@@ -175,8 +185,7 @@ test_expect_success 'omit delta with uninteresting base (same pack)' '
        # The remaining objects from the other pack are similarly not
        # reused because their objects are on the uninteresting side of
        # the query.
-       test_pack_reused 3 <trace2.txt &&
-       test_packs_reused 1 <trace2.txt
+       test_pack_objects_reused 3 1 <in
 '
 
 test_expect_success 'omit delta from uninteresting base (cross pack)' '
@@ -189,15 +198,10 @@ test_expect_success 'omit delta from uninteresting base (cross pack)' '
 
        git multi-pack-index write --bitmap --preferred-pack="pack-$P.idx" &&
 
-       : >trace2.txt &&
-       GIT_TRACE2_EVENT="$PWD/trace2.txt" \
-               git pack-objects --stdout --delta-base-offset --all >/dev/null &&
-
        packs_nr="$(find $packdir -type f -name "pack-*.pack" | wc -l)" &&
        objects_nr="$(git rev-list --count --all --objects)" &&
 
-       test_pack_reused $(($objects_nr - 1)) <trace2.txt &&
-       test_packs_reused $packs_nr <trace2.txt
+       test_pack_objects_reused_all $(($objects_nr - 1)) $packs_nr
 '
 
 test_done
index a95841dc368329286a798982ffac30b7321e02df..25772c85c5a9a89bf247cfa41cf9d8b127572cd8 100755 (executable)
@@ -24,6 +24,15 @@ setup_repository () {
        )
 }
 
+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 &&
@@ -209,4 +218,156 @@ test_expect_success 'git fetch --multiple --jobs=0 picks a default' '
         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
index 7ab220fa3135a1ecbaa8f69a5db8a9b1298f4ce1..5e566205ba4b95830bd97c97b0092cb4a3356304 100755 (executable)
@@ -771,7 +771,7 @@ test_expect_success 'fetching submodule into a broken repository' '
        git -C dst fetch --recurse-submodules &&
 
        # Break the receiving submodule
-       test-tool -C dst/sub ref-store main delete-refs REF_NO_DEREF msg HEAD &&
+       rm -r dst/sub/.git/objects &&
 
        # NOTE: without the fix the following tests will recurse forever!
        # They should terminate with an error.
index df758e187dfdc2fc869ad5cb747d895a145d5e91..71428f3d5c760a9bdca7524e15cb9747e92cfd17 100755 (executable)
@@ -232,8 +232,9 @@ test_expect_success 'push --atomic fails on server-side errors' '
        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 @@ test_expect_success 'push --atomic fails on server-side errors' '
        # --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
-       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
+       # The atomic and other branches should not be 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 &&
index e069737b80b7bd906afa1963f7b818ffac8ed2ca..a623a1058cd2acddcfca4b6712dcfa41e56dbb67 100755 (executable)
@@ -733,4 +733,22 @@ test_expect_success 'no empty path components' '
        ! 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
index 211672759a2260e5a1bb572f96aad93b3d3f91e5..127180e1c9a246769fb5bb7ede57f814abb4fc79 100755 (executable)
@@ -10,7 +10,10 @@ TEST_PASSES_SANITIZE_LEAK=true
 test_expect_success 'create repository and alternate directory' '
        test_commit 1 &&
        test_commit 2 &&
-       test_commit 3
+       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
@@ -46,9 +49,10 @@ do
                        git rev-list --objects --no-object-names \
                                HEAD ^$obj >expect.raw &&
 
-                       # Blobs are shared by all commits, so evethough a commit/tree
+                       # Blobs are shared by all commits, so evethough a commit/tree
                        # might be skipped, its blob must be accounted for.
-                       if [ $obj != "HEAD:1.t" ]; then
+                       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 &&
@@ -77,4 +81,69 @@ do
        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
index 561080bf240d6d5de7b557c6332995846f60e56e..cdc02706404b34b17b29692d72d97fab7eba58b1 100755 (executable)
@@ -878,7 +878,7 @@ test_expect_success 'broken branch creation' '
 
 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' '
index 459f0d741225522276b84a9a65caaa2e2a985bde..a9656a1ec8a6d71814a5f17157eb0e2acbd4bf98 100755 (executable)
@@ -1,10 +1,11 @@
 #!/bin/sh
 
 test_description='rev-list combining bitmaps and filters'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-bitmap.sh
 
-TEST_PASSES_SANITIZE_LEAK=true
 
 test_expect_success 'set up bitmapped repo' '
        # one commit will have bitmaps, the other will not
index 843a7fe14313b52d1bb3267d9b1f82152f3ccb96..eb6c8204e8bd0edd653d74102eb92db3da720202 100755 (executable)
@@ -1356,6 +1356,52 @@ test_expect_success '--no-sort without subsequent --sort prints expected refs' '
        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 &&
index 82f3d1ea0f25ed60900b09a454e3c98965db574f..948f1bb5f44e66b80004cce1b1ac2b9ee3bd4ac9 100755 (executable)
@@ -31,6 +31,37 @@ test_expect_success 'setup some history and refs' '
        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
index 72f8c1722ff76d332529450b4115d7afb39ed5ec..156a1efacfeabcba3cff34d1044efd522183f13d 100755 (executable)
@@ -42,11 +42,15 @@ test_expect_success setup '
        #!/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 ==="
@@ -121,7 +125,7 @@ test_expect_success 'custom merge backend' '
 
        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" &&
 
@@ -132,7 +136,8 @@ test_expect_success 'custom merge backend' '
        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
@@ -142,7 +147,7 @@ test_expect_success 'custom merge backend' '
 
        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" &&
 
@@ -159,7 +164,8 @@ test_expect_success 'custom merge backend' '
        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 &&
@@ -173,7 +179,7 @@ test_expect_success !WINDOWS 'custom merge driver that is killed with a signal'
 
        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" &&
 
index 70650521b042b29b41ddf7b0ee794095063e457f..7a3f1cb27c12b468cb8a97e80c75a86070c7c4a8 100755 (executable)
@@ -113,7 +113,7 @@ test_expect_success 'merging should conflict for non fast-forward' '
         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
@@ -154,9 +154,9 @@ test_expect_success 'merging should conflict for non fast-forward (resolution ex
          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 &&
@@ -181,9 +181,9 @@ test_expect_success 'merging should fail for ambiguous common parent' '
         ) &&
         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 &&
@@ -227,7 +227,7 @@ test_expect_success 'merging should fail for changes that are backwards' '
        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)" &&
@@ -535,7 +535,7 @@ test_expect_success 'merging should fail with no merge base' '
        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)" &&
index f6aebe92ff9d94b20670f7436c57f8107bb3404a..5ab4d41ee7c6b122b8601bf8059eecafdf081c39 100755 (executable)
@@ -396,10 +396,7 @@ test_expect_success '--prune-empty is able to prune entire branch' '
        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' '
index 05079c7246482cb5a50bf1579914ef810e309cd7..f4f3b7a677aa16539f8277ce3d4132ade0b17168 100755 (executable)
@@ -5,7 +5,7 @@ test_description='git reset --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 &&
@@ -19,42 +19,46 @@ test_expect_success PERL 'setup' '
 
 # 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_grep "Unstage" output
-'
-
-test_expect_success PERL 'git reset -p HEAD^' '
+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 '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_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_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 @@ test_expect_success PERL 'git reset -p aaaaaaaa (unknown fails)' '
 # 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
 '
 
index d20e5709f91cd7b1a0107bf4ee1df6809001bd30..88d1c8adf42eec1c219f50b9765d5b3c10d706fa 100755 (executable)
@@ -34,7 +34,7 @@ test_expect_success 'reset $file' '
        test_cmp expect actual
 '
 
-test_expect_success PERL 'reset -p' '
+test_expect_success 'reset -p' '
        rm .git/index &&
        git add a &&
        echo y >yes &&
index 2b3c363078bc06219c8b5c16b02f331b447f5102..aa2fdc31d1a672cb229457b05adcce90bd204aa6 100755 (executable)
@@ -116,7 +116,7 @@ test_expect_success 'rebasing submodule that should conflict' '
        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 &&
index 35a31acd4d7e26bc3bf8f42853d80593488c9227..46d4fb0354b13d585260587d78d27d1e9c545f1c 100755 (executable)
@@ -45,6 +45,32 @@ test_expect_success 'check names' '
        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
index 3d8500a52e50c004544c6df62cf123b8aeb62a99..bced44a0fc915f430ccc41d54fbd8bc48df2cef8 100755 (executable)
@@ -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
@@ -92,6 +91,34 @@ test_expect_success '--long fails with nothing to commit' '
        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 &&
+
+       # TODO: as for --include, the below command will fail because
+       # nothing is staged. If something was staged, it would not fail
+       # even though the provided pathspec does not match any tracked
+       # path. (However, the untracked paths that match the pathspec are
+       # not committed and only the staged changes get committed.)
+       # In either cases, no error is returned to stderr like in (--only
+       # and without --only/--include) cases. In a similar manner,
+       # "git add -u baz" also does not error out.
+       #
+       # Therefore, the below test is just to document the current behavior
+       # and is not an endorsement to the current behavior, and we may
+       # want to fix this. And when that happens, this test should be
+       # updated accordingly.
+
+       test_must_fail git commit --include -m "baz" baz 2>err &&
+       test_must_be_empty err
+'
+
 test_expect_success 'setup: non-initial commit' '
        echo bongo bongo bongo >file &&
        git commit -m next -a
@@ -117,6 +144,51 @@ test_expect_success '--long with stuff to commit returns ok' '
        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
@@ -389,6 +461,28 @@ test_expect_success 'amend commit to fix date' '
 
 '
 
+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
index 61c8e810ccede4618594570ea3b747f5c51e6d1c..b37e2018a74a7b28725fa277a95cca516daf5cbe 100755 (executable)
@@ -485,6 +485,24 @@ test_expect_success 'commit --trailer not confused by --- separator' '
        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 &&
@@ -718,6 +736,11 @@ test_expect_success 'message shows date when it is explicitly set' '
          .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 &&
index 832aff0616736025cb78d2b35e3823594b543b1d..ec9c6de114fddfdb7127434ae17f3ae419164f6c 100755 (executable)
@@ -1916,4 +1916,23 @@ test_expect_success 'suppress --- handling' '
        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_done
index 998a2103c7440f00788ec4c0ef607f8c48781660..b4de10a5ddac0a2a890adaeb559f1b2afea15bb3 100755 (executable)
@@ -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 &&
index 78503158fd699d2df75e8fbbbaf164a129f2578b..363f9dc0e41b2686aa9892d5e21e153ca54727b9 100755 (executable)
@@ -978,7 +978,7 @@ test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
        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
index 6a36be1e63c2bb84871e521349917be30ec6b3e3..96ae5d5880d6a597d0c553c4535081c2e51356df 100755 (executable)
@@ -91,58 +91,67 @@ test_expect_success 'difftool forwards arguments to diff' '
        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} 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} 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} 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} 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} 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} 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} 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} 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 &&
index 00d29871e659ccb34ef7204017ab07dbefa83c84..0943dfa18a3f97b7e351346d089efdd878f6ca68 100755 (executable)
@@ -67,6 +67,51 @@ test_expect_success 'maintenance.auto config option' '
        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 &&
index 348cc40658235239c7e68671738cd030871451ad..d5b98e615bca6647761e0e6f046d474ea8275015 100755 (executable)
@@ -196,4 +196,15 @@ EOF
        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
index 09606f1b3cfeb4244de4fbc12e4e96ddb7fd7de5..926ac8143943c91493eb9be0e42ca365fae56971 100755 (executable)
@@ -20,11 +20,7 @@ test_expect_success 'empty directories exist' '
                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 @@ test_expect_success 'option automkdirs set to false' '
                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 'more emptiness' '
 
 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 @@ test_expect_success 'git svn mkdirs recreates empty directories' '
                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 @@ test_expect_success 'git svn mkdirs -r works' '
                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 @@ test_expect_success 'empty directories in trunk exist' '
                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 'remove a top-level directory from svn' '
 
 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 @@ test_expect_success 'git svn gc-ed files work' '
                        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
        )
index 4432a30d10b69a168ca477f222bfa52d36447006..428339e342751e02e3c9fe5b8053c0ccc6fee4b8 100755 (executable)
@@ -154,7 +154,14 @@ test_expect_success 'scalar clone' '
                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 &&
index aa9a614de33a17f886b7517daeabad5f48bdc42c..b16c284181b8707ed21c85efa0e0a0fea128faae 100755 (executable)
@@ -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 @@ test_completion ()
        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.
@@ -1259,6 +1272,142 @@ test_expect_success 'git switch - with no options, complete local branches and u
        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
@@ -1925,6 +2074,14 @@ test_expect_success 'git checkout - --orphan with branch already provided comple
        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 &&
@@ -2567,6 +2724,35 @@ test_expect_success 'git config - variable name include' '
        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
@@ -2720,4 +2906,31 @@ test_expect_success '__git_complete' '
        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
index 970c6538cba7a8d9465b6774b23e367c4b257d88..33405c90d740d4c4e4fac93cfa8a713939a39348 100644 (file)
@@ -42,8 +42,8 @@ finalize_test_case_output () {
        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
index fc93aa57e6116d5602b17a63f41bd4b8746289e2..c8af8dab795604998475e5fdff21137534c7f39b 100644 (file)
@@ -1297,6 +1297,11 @@ test_done () {
                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)"
@@ -1750,6 +1755,8 @@ esac
 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
diff --git a/t/unit-tests/t-ctype.c b/t/unit-tests/t-ctype.c
new file mode 100644 (file)
index 0000000..f315489
--- /dev/null
@@ -0,0 +1,80 @@
+#include "test-lib.h"
+
+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);
+}
+
+/* Macro to test a character type */
+#define TEST_CTYPE_FUNC(func, string) \
+static void test_ctype_##func(void) { \
+       for (int i = 0; i < 256; i++) { \
+               if (!check_int(func(i), ==, is_in(string, i))) \
+                       test_msg("       i: 0x%02x", i); \
+       } \
+       if (!check(!func(EOF))) \
+                       test_msg("      i: 0x%02x (EOF)", EOF); \
+}
+
+#define TEST_CHAR_CLASS(class) TEST(test_ctype_##class(), #class " works")
+
+#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"
+
+TEST_CTYPE_FUNC(isdigit, DIGIT)
+TEST_CTYPE_FUNC(isspace, " \n\r\t")
+TEST_CTYPE_FUNC(isalpha, LOWER UPPER)
+TEST_CTYPE_FUNC(isalnum, LOWER UPPER DIGIT)
+TEST_CTYPE_FUNC(is_glob_special, "*?[\\")
+TEST_CTYPE_FUNC(is_regex_special, "$()*+.?[\\^{|")
+TEST_CTYPE_FUNC(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~")
+TEST_CTYPE_FUNC(isascii, ASCII)
+TEST_CTYPE_FUNC(islower, LOWER)
+TEST_CTYPE_FUNC(isupper, UPPER)
+TEST_CTYPE_FUNC(iscntrl, CNTRL)
+TEST_CTYPE_FUNC(ispunct, PUNCT)
+TEST_CTYPE_FUNC(isxdigit, DIGIT "abcdefABCDEF")
+TEST_CTYPE_FUNC(isprint, LOWER UPPER DIGIT PUNCT " ")
+
+int cmd_main(int argc, const char **argv) {
+       /* Run all character type tests */
+       TEST_CHAR_CLASS(isspace);
+       TEST_CHAR_CLASS(isdigit);
+       TEST_CHAR_CLASS(isalpha);
+       TEST_CHAR_CLASS(isalnum);
+       TEST_CHAR_CLASS(is_glob_special);
+       TEST_CHAR_CLASS(is_regex_special);
+       TEST_CHAR_CLASS(is_pathspec_magic);
+       TEST_CHAR_CLASS(isascii);
+       TEST_CHAR_CLASS(islower);
+       TEST_CHAR_CLASS(isupper);
+       TEST_CHAR_CLASS(iscntrl);
+       TEST_CHAR_CLASS(ispunct);
+       TEST_CHAR_CLASS(isxdigit);
+       TEST_CHAR_CLASS(isprint);
+
+       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 (file)
index 0000000..d78b002
--- /dev/null
@@ -0,0 +1,98 @@
+#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, int *result, size_t input_size)
+{
+       struct prio_queue pq = { intcmp };
+
+       for (int i = 0, j = 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_int(result[j++], ==, show(get)))
+                               test_msg("failed at result[] index %d", j-1);
+                       break;
+               case DUMP:
+                       while ((peek = prio_queue_peek(&pq))) {
+                               get = prio_queue_get(&pq);
+                               if (!check(peek == get))
+                                       return;
+                               if(!check_int(result[j++], ==, show(get)))
+                                       test_msg("failed at result[] index %d", j-1);
+                       }
+                       break;
+               case STACK:
+                       pq.compare = NULL;
+                       break;
+               case REVERSE:
+                       prio_queue_reverse(&pq);
+                       break;
+               default:
+                       prio_queue_put(&pq, &input[i]);
+                       break;
+               }
+       }
+       clear_prio_queue(&pq);
+}
+
+#define BASIC_INPUT 2, 6, 3, 10, 9, 5, 7, 4, 5, 8, 1, DUMP
+#define BASIC_RESULT 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10
+
+#define MIXED_PUT_GET_INPUT 6, 2, 4, GET, 5, 3, GET, GET, 1, DUMP
+#define MIXED_PUT_GET_RESULT 2, 3, 4, 1, 5, 6
+
+#define EMPTY_QUEUE_INPUT 1, 2, GET, GET, GET, 1, 2, GET, GET, GET
+#define EMPTY_QUEUE_RESULT 1, 2, MISSING, 1, 2, MISSING
+
+#define STACK_INPUT STACK, 8, 1, 5, 4, 6, 2, 3, DUMP
+#define STACK_RESULT 3, 2, 6, 4, 5, 1, 8
+
+#define REVERSE_STACK_INPUT STACK, 1, 2, 3, 4, 5, 6, REVERSE, DUMP
+#define REVERSE_STACK_RESULT 1, 2, 3, 4, 5, 6
+
+#define TEST_INPUT(INPUT, RESULT, name)                        \
+  static void test_##name(void)                                \
+{                                                              \
+       int input[] = {INPUT};                                  \
+       int result[] = {RESULT};                                \
+       test_prio_queue(input, result, ARRAY_SIZE(input));      \
+}
+
+TEST_INPUT(BASIC_INPUT, BASIC_RESULT, basic)
+TEST_INPUT(MIXED_PUT_GET_INPUT, MIXED_PUT_GET_RESULT, mixed)
+TEST_INPUT(EMPTY_QUEUE_INPUT, EMPTY_QUEUE_RESULT, empty)
+TEST_INPUT(STACK_INPUT, STACK_RESULT, stack)
+TEST_INPUT(REVERSE_STACK_INPUT, REVERSE_STACK_RESULT, reverse)
+
+int cmd_main(int argc, const char **argv)
+{
+       TEST(test_basic(), "prio-queue works for basic input");
+       TEST(test_mixed(), "prio-queue works for mixed put & get commands");
+       TEST(test_empty(), "prio-queue works when queue is empty");
+       TEST(test_stack(), "prio-queue works when used as a LIFO stack");
+       TEST(test_reverse(), "prio-queue works when LIFO stack is reversed");
+
+       return test_done();
+}
index 7bf9dfdb9595512f42357639c0eda40ad608bb34..66d6980ffbefd83ed350c323bf946b29cb54dbf2 100644 (file)
@@ -21,12 +21,11 @@ static struct {
        .result = RESULT_NONE,
 };
 
-#ifndef _MSC_VER
-#define make_relative(location) location
-#else
 /*
  * 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"
 
@@ -34,32 +33,66 @@ 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;
 
-       if (!prefix_len) {
+       /* one-time preparation */
+       if (need_bs_to_fs < 0) {
                size_t len = strlen(prefix);
-               const char *needle = "\\t\\unit-tests\\test-lib.c";
+               char needle[] = "t\\unit-tests\\test-lib.c";
                size_t needle_len = strlen(needle);
 
-               if (len < needle_len || strcmp(needle, prefix + len - needle_len))
-                       die("unexpected suffix of '%s'", prefix);
+               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;
+               }
 
-               /* let it end in a directory separator */
-               prefix_len = len - needle_len + 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? */
-       if (fspathncmp(location, prefix, prefix_len))
+       /*
+        * 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;
 
-       strlcpy(buf, location + prefix_len, sizeof(buf));
+       /*
+        * 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;
 }
-#endif
 
 static void msg_with_prefix(const char *prefix, const char *format, va_list ap)
 {
index 3a0710a4583a4a11fa5a87cda88ef6a3d0610870..ef9df4af558fbb18849279cff2e67507026af106 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -845,16 +845,15 @@ static size_t find_end_of_log_message(const char *input, int no_divider)
        /* Assume the naive end of the input is already what we want. */
        end = strlen(input);
 
-       if (no_divider)
-               return end;
-
        /* Optionally skip over any patch part ("---" line and below). */
-       for (s = input; *s; s = next_line(s)) {
-               const char *v;
+       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;
+                       if (skip_prefix(s, "---", &v) && isspace(*v)) {
+                               end = s - input;
+                               break;
+                       }
                }
        }
 
index e34a8f47cfbf5cba150e1aa7c6267fc2b1c080c0..dd6002b393738df81c7ecfc85deb4ed0abe4943c 100644 (file)
@@ -17,6 +17,7 @@
 #include "refspec.h"
 #include "transport-internal.h"
 #include "protocol.h"
+#include "packfile.h"
 
 static int debug;
 
@@ -432,6 +433,8 @@ static int fetch_with_fetch(struct transport *transport,
                        warning(_("%s unexpectedly said: '%s'"), data->name, buf.buf);
        }
        strbuf_release(&buf);
+
+       reprepare_packed_git(the_repository);
        return 0;
 }
 
@@ -626,7 +629,8 @@ static int process_connect_service(struct transport *transport,
                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)
@@ -643,6 +647,7 @@ static int process_connect(struct transport *transport,
        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)
@@ -650,7 +655,10 @@ static int process_connect(struct transport *transport,
        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,
@@ -660,14 +668,14 @@ static int connect_helper(struct transport *transport, const char *name,
 
        /* 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;
 }
 
@@ -682,10 +690,8 @@ static int fetch_refs(struct transport *transport,
 
        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
@@ -1142,10 +1148,8 @@ static int push_refs(struct transport *transport,
 {
        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,
@@ -1186,11 +1190,9 @@ static struct ref *get_refs_list(struct transport *transport, int for_push,
 {
        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);
 }
@@ -1274,10 +1276,8 @@ static int get_bundle_uri(struct transport *transport)
 {
        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;
 }
index bd7899e9bf5550b2b333dbbcf85a5b98c7813ad2..df518ead70c387dd354dd69a5ee4af8a3ab0d560 100644 (file)
@@ -1467,6 +1467,7 @@ int transport_push(struct repository *r,
        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:
index b517792ba23a2104f5f8207519e64cbc5d2daaa4..690fa6569bd7fe03ca104e3e789fd67b58e41270 100644 (file)
@@ -100,7 +100,7 @@ void *fill_tree_descriptor(struct repository *r,
        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);
        return buf;
index 2537affa90762228a19d305be749f410d5afb5dc..6e0d441ef523edaeca10ffdf2e13f8d52ee592d0 100644 (file)
@@ -463,7 +463,7 @@ static void create_pack_file(struct upload_pack_data *pack_data,
 
  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);
 }
index e399543823bdf2c0c8f24fb87f7622ac4d8a366b..92ef649c99ef49d1b21688584ad7d3bbbff6b737 100644 (file)
@@ -3,6 +3,7 @@
 #include "userdiff.h"
 #include "attr.h"
 #include "strbuf.h"
+#include "environment.h"
 
 static struct userdiff_driver *drivers;
 static int ndrivers;
@@ -323,8 +324,7 @@ static int userdiff_find_by_namelen_cb(struct userdiff_driver *driver,
 {
        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 */
        }
@@ -460,7 +460,8 @@ struct userdiff_driver *userdiff_get_textconv(struct repository *r,
        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;
 
index dbc670d369588caedafa646401b037675d4f05af..b02a05a74a341157fa8dff7da22936127bebf18e 100644 (file)
 #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);
 }
 
@@ -75,8 +80,8 @@ static struct worktree *get_main_worktree(int skip_reading_head)
        return worktree;
 }
 
-static struct worktree *get_linked_worktree(const char *id,
-                                           int skip_reading_head)
+struct worktree *get_linked_worktree(const char *id,
+                                    int skip_reading_head)
 {
        struct worktree *worktree = NULL;
        struct strbuf path = STRBUF_INIT;
index ce45b66de9e82d4d619b6f74e0e77c8bd5ab3f53..f14784a2ff871c69e5b5e28466439201721407b2 100644 (file)
@@ -57,6 +57,13 @@ struct worktree *find_worktree(struct worktree **list,
                               const char *prefix,
                               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.
@@ -134,6 +141,11 @@ void repair_worktrees(worktree_repair_fn, void *cb_data);
  */
 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)
  */
index 39421528653e29cbd724153edf94d2613ea1d6b4..01a9a51fa2fcd758b32ffc8f3e75811710804754 100644 (file)
  */
 void maybe_flush_or_die(FILE *f, const char *desc)
 {
-       static int skip_stdout_flush = -1;
-
        if (f == stdout) {
-               if (skip_stdout_flush < 0) {
-                       skip_stdout_flush = git_env_bool("GIT_FLUSH", -1);
-                       if (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))
-                                       skip_stdout_flush = 0;
+                                       force_flush_stdout = 1;
                                else
-                                       skip_stdout_flush = S_ISREG(st.st_mode);
+                                       force_flush_stdout = !S_ISREG(st.st_mode);
                        }
                }
-               if (skip_stdout_flush && !ferror(f))
+               if (!force_flush_stdout && !ferror(f))
                        return;
        }
        if (fflush(f)) {
index b5a29083df503e3adfb6f2e4a74e55d4f44514b6..7108a92b52ce06e5674aadfc5c89d05157a9db93 100644 (file)
@@ -1107,12 +1107,15 @@ void wt_status_append_cut_line(struct strbuf *buf)
        strbuf_add_commented_lines(buf, explanation, strlen(explanation), comment_line_char);
 }
 
-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);
 }
 
@@ -1143,11 +1146,12 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
         * 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 */
index 819dcad72300c56900ac05f3943d27dd2b9ed7a4..5e99ba47073493aa4ee80a9501dd4375ec903d8f 100644 (file)
@@ -130,6 +130,7 @@ struct wt_status {
        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 */
 
@@ -147,7 +148,7 @@ struct wt_status {
 
 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);