aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.editorconfig2
-rw-r--r--.gitattributes6
-rw-r--r--.github/workflows/main.yml4
-rw-r--r--.gitignore1
-rw-r--r--Documentation/.gitattributes1
-rw-r--r--Documentation/BreakingChanges.adoc17
-rw-r--r--Documentation/CodingGuidelines4
-rw-r--r--Documentation/Makefile7
-rw-r--r--Documentation/MyFirstContribution.adoc25
-rw-r--r--Documentation/MyFirstObjectWalk.adoc14
-rw-r--r--Documentation/RelNotes/2.49.0.adoc14
-rw-r--r--Documentation/RelNotes/2.50.0.adoc126
-rw-r--r--Documentation/ToolsForGit.adoc1
-rwxr-xr-xDocumentation/build-docdep.perl24
-rw-r--r--Documentation/config/branch.adoc105
-rw-r--r--Documentation/config/http.adoc18
-rw-r--r--Documentation/config/promisor.adoc27
-rw-r--r--Documentation/config/remote.adoc22
-rw-r--r--Documentation/fsck-msgids.adoc14
-rw-r--r--Documentation/git-bisect.adoc1
-rw-r--r--Documentation/git-branch.adoc287
-rw-r--r--Documentation/git-cat-file.adoc4
-rw-r--r--Documentation/git-check-attr.adoc6
-rw-r--r--Documentation/git-clone.adoc6
-rw-r--r--Documentation/git-column.adoc3
-rw-r--r--Documentation/git-cvsserver.adoc4
-rw-r--r--Documentation/git-diff-pairs.adoc60
-rw-r--r--Documentation/git-fast-export.adoc26
-rw-r--r--Documentation/git-fast-import.adoc20
-rw-r--r--Documentation/git-for-each-ref.adoc2
-rw-r--r--Documentation/git-fsck.adoc7
-rw-r--r--Documentation/git-p4.adoc14
-rw-r--r--Documentation/git-pack-refs.adoc4
-rw-r--r--Documentation/git-rebase.adoc3
-rw-r--r--Documentation/git-repack.adoc21
-rw-r--r--Documentation/git-restore.adoc3
-rw-r--r--Documentation/gitattributes.adoc30
-rw-r--r--Documentation/gitcli.adoc2
-rw-r--r--Documentation/gitprotocol-common.adoc2
-rw-r--r--Documentation/gitprotocol-v2.adoc54
-rw-r--r--Documentation/gitrepository-layout.adoc4
-rw-r--r--Documentation/gitweb.adoc11
-rw-r--r--Documentation/gitweb.conf.adoc2
-rwxr-xr-xDocumentation/howto/howto-index.sh14
-rw-r--r--Documentation/howto/meson.build34
-rw-r--r--Documentation/howto/new-command.adoc2
-rw-r--r--Documentation/merge-strategies.adoc12
-rw-r--r--Documentation/mergetools/vimdiff.adoc2
-rw-r--r--Documentation/meson.build19
-rw-r--r--Documentation/rev-list-options.adoc4
-rw-r--r--Documentation/technical/api-simple-ipc.adoc2
-rw-r--r--Documentation/technical/hash-function-transition.adoc2
-rw-r--r--Documentation/technical/large-object-promisors.adoc656
-rw-r--r--Documentation/technical/meson.build62
-rw-r--r--Documentation/technical/multi-pack-index.adoc82
-rw-r--r--Documentation/technical/partial-clone.adoc2
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile18
-rw-r--r--README.md14
l---------RelNotes2
-rw-r--r--advice.h2
-rw-r--r--apply.c2
-rw-r--r--bisect.c2
-rw-r--r--branch.c7
-rw-r--r--builtin.h3
-rw-r--r--builtin/am.c7
-rw-r--r--builtin/checkout-index.c43
-rw-r--r--builtin/clone.c6
-rw-r--r--builtin/commit.c8
-rw-r--r--builtin/config.c8
-rw-r--r--builtin/diff-pairs.c207
-rw-r--r--builtin/fast-export.c188
-rw-r--r--builtin/fast-import.c27
-rw-r--r--builtin/fetch.c46
-rw-r--r--builtin/for-each-ref.c5
-rw-r--r--builtin/fsck.c43
-rw-r--r--builtin/gc.c25
-rw-r--r--builtin/index-pack.c1
-rw-r--r--builtin/init-db.c8
-rw-r--r--builtin/log.c6
-rw-r--r--builtin/ls-files.c32
-rw-r--r--builtin/name-rev.c10
-rw-r--r--builtin/notes.c9
-rw-r--r--builtin/pack-objects.c121
-rw-r--r--builtin/pack-refs.c8
-rw-r--r--builtin/pull.c3
-rw-r--r--builtin/rebase.c2
-rw-r--r--builtin/receive-pack.c4
-rw-r--r--builtin/refs.c4
-rw-r--r--builtin/remote.c6
-rw-r--r--builtin/repack.c62
-rw-r--r--builtin/replace.c2
-rw-r--r--builtin/rerere.c11
-rw-r--r--builtin/rev-parse.c6
-rw-r--r--builtin/send-pack.c7
-rw-r--r--builtin/stash.c1
-rw-r--r--builtin/submodule--helper.c4
-rw-r--r--builtin/tag.c2
-rw-r--r--builtin/unpack-objects.c1
-rw-r--r--builtin/update-ref.c15
-rw-r--r--builtin/verify-commit.c13
-rw-r--r--builtin/verify-tag.c7
-rw-r--r--builtin/worktree.c43
-rw-r--r--bulk-checkin.c16
-rwxr-xr-xci/check-unsafe-assertions.sh18
-rwxr-xr-xci/run-static-analysis.sh2
-rwxr-xr-xci/test-documentation.sh27
-rw-r--r--command-list.txt1
-rw-r--r--commit-graph.c2
-rw-r--r--commit.c16
-rw-r--r--compat/mingw-posix.h435
-rw-r--r--compat/mingw.c44
-rw-r--r--compat/mingw.h426
-rw-r--r--compat/msvc-posix.h33
-rw-r--r--compat/msvc.h30
-rw-r--r--compat/posix.h541
-rw-r--r--compat/precompose_utf8.c6
-rw-r--r--compiler-tricks/not-constant.c2
-rw-r--r--config.c25
-rw-r--r--config.h9
-rw-r--r--config.mak.dev1
-rw-r--r--connect.c9
-rw-r--r--contrib/buildsystems/CMakeLists.txt12
-rw-r--r--contrib/coccinelle/meson.build7
-rw-r--r--contrib/completion/git-completion.bash49
-rw-r--r--contrib/contacts/Makefile2
-rw-r--r--contrib/contacts/git-contacts.adoc (renamed from contrib/contacts/git-contacts.txt)0
-rw-r--r--contrib/contacts/meson.build4
-rwxr-xr-xcontrib/long-running-filter/example.pl2
-rw-r--r--contrib/subtree/Makefile2
-rw-r--r--contrib/subtree/git-subtree.adoc (renamed from contrib/subtree/git-subtree.txt)0
-rw-r--r--contrib/subtree/meson.build4
-rw-r--r--copy.c4
-rw-r--r--decorate.c15
-rw-r--r--diff.c72
-rw-r--r--diff.h35
-rw-r--r--diffcore-rename.c11
-rw-r--r--diffcore.h2
-rw-r--r--dir-iterator.c24
-rw-r--r--dir-iterator.h11
-rw-r--r--dir.c2
-rw-r--r--dir.h1
-rw-r--r--editor.c6
-rw-r--r--environment.c27
-rw-r--r--environment.h11
-rw-r--r--ewah/ewah_bitmap.c33
-rw-r--r--ewah/ewok.h12
-rw-r--r--fsck.h6
-rwxr-xr-xgenerate-configlist.sh16
-rw-r--r--git-compat-util.h549
-rw-r--r--git-curl-compat.h7
-rw-r--r--git-zlib.c27
-rw-r--r--git.c1
-rw-r--r--hash.h23
-rw-r--r--help.c5
-rw-r--r--hook.c3
-rw-r--r--http-backend.c2
-rw-r--r--http.c75
-rw-r--r--ident.c2
-rw-r--r--imap-send.c2
-rw-r--r--iterator.h2
-rw-r--r--list-objects-filter-options.h2
-rw-r--r--loose.c8
-rw-r--r--merge-ort-wrappers.c72
-rw-r--r--merge-ort-wrappers.h12
-rw-r--r--merge-ort.c70
-rw-r--r--merge-ort.h5
-rw-r--r--merge-recursive.c2
-rw-r--r--mergetools/vimdiff14
-rw-r--r--meson.build25
-rw-r--r--midx-write.c59
-rw-r--r--notes-merge.c26
-rw-r--r--object-file.c64
-rw-r--r--object-name.c20
-rw-r--r--object-name.h6
-rw-r--r--pack-bitmap-write.c67
-rw-r--r--pack-bitmap.c344
-rw-r--r--pack-bitmap.h4
-rw-r--r--pack-revindex.c34
-rw-r--r--pack-write.c10
-rw-r--r--packfile.c3
-rw-r--r--packfile.h2
-rw-r--r--parallel-checkout.c2
-rw-r--r--parse-options.h2
-rw-r--r--path.c166
-rw-r--r--path.h191
-rw-r--r--po/bg.po446
-rw-r--r--po/de.po447
-rw-r--r--po/fr.po452
-rw-r--r--po/id.po520
-rw-r--r--po/it.po2
-rw-r--r--po/ko.po3
-rw-r--r--po/sv.po539
-rw-r--r--po/tr.po436
-rw-r--r--po/uk.po469
-rw-r--r--po/vi.po533
-rw-r--r--po/zh_CN.po540
-rw-r--r--po/zh_TW.po574
-rw-r--r--promisor-remote.c247
-rw-r--r--promisor-remote.h37
-rw-r--r--pseudo-merge.h4
-rw-r--r--reachable.c6
-rw-r--r--read-cache.c26
-rw-r--r--refs.c213
-rw-r--r--refs.h14
-rw-r--r--refs/debug.c20
-rw-r--r--refs/files-backend.c127
-rw-r--r--refs/iterator.c150
-rw-r--r--refs/packed-backend.c455
-rw-r--r--refs/ref-cache.c88
-rw-r--r--refs/refs-internal.h53
-rw-r--r--refs/reftable-backend.c94
-rw-r--r--refspec.c58
-rw-r--r--refspec.h18
-rw-r--r--reftable/basics.c19
-rw-r--r--reftable/basics.h123
-rw-r--r--reftable/block.c29
-rw-r--r--reftable/block.h2
-rw-r--r--reftable/blocksource.c17
-rw-r--r--reftable/iter.c13
-rw-r--r--reftable/merged.c27
-rw-r--r--reftable/pq.c40
-rw-r--r--reftable/pq.h2
-rw-r--r--reftable/reader.c35
-rw-r--r--reftable/record.c137
-rw-r--r--reftable/record.h6
-rw-r--r--reftable/stack.c52
-rw-r--r--reftable/system.c7
-rw-r--r--reftable/system.h9
-rw-r--r--reftable/writer.c61
-rw-r--r--remote.c25
-rw-r--r--repo-settings.c44
-rw-r--r--repo-settings.h14
-rw-r--r--repository.c6
-rw-r--r--rerere.c101
-rw-r--r--rerere.h3
-rw-r--r--revision.c7
-rw-r--r--run-command.c8
-rw-r--r--scalar.c4
-rw-r--r--sequencer.c2
-rw-r--r--serve.c26
-rw-r--r--server-info.c2
-rw-r--r--setup.c72
-rw-r--r--shallow.c4
-rw-r--r--simple-ipc.h2
-rw-r--r--submodule.c15
-rw-r--r--submodule.h3
-rw-r--r--t/README25
-rw-r--r--t/helper/test-dir-iterator.c1
-rw-r--r--t/helper/test-path-utils.c19
-rw-r--r--t/helper/test-ref-store.c7
-rw-r--r--t/helper/test-rot13-filter.c2
-rw-r--r--t/meson.build10
-rwxr-xr-xt/t0091-bugreport.sh3
-rwxr-xr-xt/t0450-txt-doc-vs-help.sh50
-rw-r--r--t/t0450/adoc-help-mismatches (renamed from t/t0450/txt-help-mismatches)0
-rwxr-xr-xt/t0602-reffiles-fsck.sh1209
-rwxr-xr-xt/t0610-reftable-basics.sh7
-rwxr-xr-xt/t1006-cat-file.sh53
-rwxr-xr-xt/t1419-exclude-refs.sh26
-rwxr-xr-xt/t2006-checkout-index-basic.sh7
-rwxr-xr-xt/t3004-ls-files-basic.sh7
-rwxr-xr-xt/t3650-replay-basics.sh22
-rwxr-xr-xt/t4055-diff-context.sh10
-rwxr-xr-xt/t4070-diff-pairs.sh90
-rwxr-xr-xt/t4151-am-abort.sh2
-rwxr-xr-xt/t4206-log-follow-harder-copies.sh32
-rwxr-xr-xt/t4255-am-submodule.sh1
-rwxr-xr-xt/t4301-merge-tree-write-tree.sh6
-rwxr-xr-xt/t5323-pack-redundant.sh2
-rwxr-xr-xt/t5329-pack-objects-cruft.sh302
-rwxr-xr-xt/t5334-incremental-multi-pack-index.sh87
-rwxr-xr-xt/t5400-send-pack.sh7
-rwxr-xr-xt/t5503-tagfollow.sh14
-rwxr-xr-xt/t5505-remote.sh6
-rwxr-xr-xt/t5515-fetch-merge-logic.sh2
-rwxr-xr-xt/t5516-fetch-push.sh20
-rwxr-xr-xt/t5702-protocol-v2.sh44
-rwxr-xr-xt/t5710-promisor-remote-capability.sh373
-rwxr-xr-xt/t6012-rev-list-simplify.sh2
-rwxr-xr-xt/t6120-describe.sh18
-rwxr-xr-xt/t6300-for-each-ref.sh7
-rwxr-xr-xt/t6423-merge-rename-directories.sh47
-rwxr-xr-xt/t6427-diff3-conflict-markers.sh2
-rwxr-xr-xt/t6434-merge-recursive-rename-options.sh2
-rwxr-xr-xt/t7030-verify-tag.sh7
-rwxr-xr-xt/t7510-signed-commit.sh7
-rwxr-xr-xt/t7615-diff-algo-with-mergy-operations.sh4
-rwxr-xr-xt/t7704-repack-cruft.sh349
-rwxr-xr-xt/t9350-fast-export.sh116
-rwxr-xr-xt/t9902-completion.sh206
-rw-r--r--t/test-lib-functions.sh5
-rw-r--r--t/test-lib.sh7
-rw-r--r--t/unit-tests/lib-oid.c32
-rw-r--r--t/unit-tests/lib-oid.h9
-rw-r--r--t/unit-tests/t-oid-array.c126
-rw-r--r--t/unit-tests/t-oidmap.c181
-rw-r--r--t/unit-tests/t-oidtree.c122
-rw-r--r--t/unit-tests/t-reftable-basics.c28
-rw-r--r--t/unit-tests/t-reftable-pq.c22
-rw-r--r--t/unit-tests/t-reftable-record.c42
-rw-r--r--t/unit-tests/u-oid-array.c129
-rw-r--r--t/unit-tests/u-oidmap.c136
-rw-r--r--t/unit-tests/u-oidtree.c107
-rw-r--r--t/unit-tests/unit-test.c2
-rw-r--r--tempfile.c4
-rw-r--r--tmp-objdir.c17
-rw-r--r--trace2.h2
-rw-r--r--trace2/tr2_sysenv.c2
-rw-r--r--transport-helper.c2
-rw-r--r--transport.h2
-rw-r--r--unpack-trees.c2
-rw-r--r--upload-pack.c3
-rw-r--r--worktree.c54
-rw-r--r--worktree.h10
-rw-r--r--wt-status.c42
-rw-r--r--xdiff/xdiff.h2
-rw-r--r--xdiff/xemit.c8
-rw-r--r--xdiff/xpatience.c2
319 files changed, 12834 insertions, 5842 deletions
diff --git a/.editorconfig b/.editorconfig
index a3c578a43c..2d3929b591 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,7 +4,7 @@ insert_final_newline = true
# The settings for C (*.c and *.h) files are mirrored in .clang-format. Keep
# them in sync.
-[{*.{c,h,sh,perl,pl,pm,txt,adoc},config.mak.*,Makefile}]
+[{*.{c,h,sh,bash,perl,pl,pm,txt,adoc},config.mak.*,Makefile}]
indent_style = tab
tab_width = 8
diff --git a/.gitattributes b/.gitattributes
index 43fa883a84..c6a0b35116 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -12,7 +12,7 @@ CODE_OF_CONDUCT.md -whitespace
/GIT-VERSION-GEN text eol=lf
/mergetools/* text eol=lf
/t/oid-info/* text eol=lf
-/Documentation/git-merge.txt conflict-marker-size=32
-/Documentation/gitk.txt conflict-marker-size=32
-/Documentation/user-manual.txt conflict-marker-size=32
+/Documentation/git-merge.adoc conflict-marker-size=32
+/Documentation/gitk.adoc conflict-marker-size=32
+/Documentation/user-manual.adoc conflict-marker-size=32
/t/t????-*.sh conflict-marker-size=32
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 9959b61ece..37541f3d10 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -349,6 +349,7 @@ jobs:
if: needs.ci-config.outputs.enabled == 'yes'
env:
CC: clang
+ CI_JOB_IMAGE: ubuntu-latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -432,6 +433,7 @@ jobs:
if: needs.ci-config.outputs.enabled == 'yes'
env:
jobname: StaticAnalysis
+ CI_JOB_IMAGE: ubuntu-22.04
runs-on: ubuntu-22.04
concurrency:
group: static-analysis-${{ github.ref }}
@@ -446,6 +448,7 @@ jobs:
if: needs.ci-config.outputs.enabled == 'yes'
env:
jobname: sparse
+ CI_JOB_IMAGE: ubuntu-20.04
runs-on: ubuntu-20.04
concurrency:
group: sparse-${{ github.ref }}
@@ -473,6 +476,7 @@ jobs:
cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
env:
jobname: Documentation
+ CI_JOB_IMAGE: ubuntu-latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
diff --git a/.gitignore b/.gitignore
index 08a66ca508..04c444404e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,6 +55,7 @@
/git-diff
/git-diff-files
/git-diff-index
+/git-diff-pairs
/git-diff-tree
/git-difftool
/git-difftool--helper
diff --git a/Documentation/.gitattributes b/Documentation/.gitattributes
deleted file mode 100644
index ddb030137d..0000000000
--- a/Documentation/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-*.txt whitespace
diff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc
index 042709a461..61bdd586b9 100644
--- a/Documentation/BreakingChanges.adoc
+++ b/Documentation/BreakingChanges.adoc
@@ -66,22 +66,21 @@ changes are made at a certain version boundary, and recording these
decisions in this document, are necessary but not sufficient.
Because such changes are expected to be numerous, and the design and
implementation of them are expected to span over time, they have to
-be deployable trivially at such a version boundary.
+be deployable trivially at such a version boundary, prepared over long
+time.
The breaking changes MUST be guarded with the a compile-time switch,
WITH_BREAKING_CHANGES, to help this process. When built with it,
the resulting Git binary together with its documentation would
behave as if these breaking changes slated for the next big version
-boundary are already in effect. We may also want to have a CI job
-or two to exercise the work-in-progress version of Git with these
-breaking changes.
+boundary are already in effect. We also have a CI job to exercise
+the work-in-progress version of Git with these breaking changes.
== Git 3.0
The following subsections document upcoming breaking changes for Git 3.0. There
-is no planned release date for this breaking version yet. The early
-adopter configuration used for changes for this release is `feature.git3`.
+is no planned release date for this breaking version yet.
Proposed changes and removals only include items which are "ready" to be done.
In other words, this is not supposed to be a wishlist of features that should
@@ -179,6 +178,12 @@ references.
+
These features will be removed.
+* Support for "--stdin" option in the "name-rev" command was
+ deprecated (and hidden from the documentation) in the Git 2.40
+ timeframe, in preference to its synonym "--annotate-stdin". Git 3.0
+ removes the support for "--stdin" altogether.
+
+
== Superseded features that will not be deprecated
Some features have gained newer replacements that aim to improve the design in
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index ba047ed224..a0e7041c54 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -44,7 +44,7 @@ code are expected to match the style the surrounding code already
uses (even if it doesn't match the overall style of existing code).
But if you must have a list of rules, here are some language
-specific ones. Note that Documentation/ToolsForGit.txt document
+specific ones. Note that Documentation/ToolsForGit.adoc document
has a collection of tips to help you use some external tools
to conform to these guidelines.
@@ -755,7 +755,7 @@ Externally Visible Names
Writing Documentation:
Most (if not all) of the documentation pages are written in the
- AsciiDoc format in *.txt files (e.g. Documentation/git.txt), and
+ AsciiDoc format in *.adoc files (e.g. Documentation/git.adoc), and
processed into HTML and manpages (e.g. git.html and git.1 in the
same directory).
diff --git a/Documentation/Makefile b/Documentation/Makefile
index c9a7cf662f..0d3a2c6bfe 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -109,6 +109,7 @@ SP_ARTICLES += howto/coordinate-embargoed-releases
API_DOCS = $(patsubst %.adoc,%,$(filter-out technical/api-index-skel.adoc technical/api-index.adoc, $(wildcard technical/api-*.adoc)))
SP_ARTICLES += $(API_DOCS)
+TECH_DOCS += BreakingChanges
TECH_DOCS += DecisionMaking
TECH_DOCS += ReviewingGuidelines
TECH_DOCS += MyFirstContribution
@@ -224,6 +225,10 @@ asciidoc.conf: asciidoc.conf.in FORCE
$(QUIET_GEN)$(call version_gen,"$(shell pwd)/..",$<,$@)
endif
+ifdef WITH_BREAKING_CHANGES
+ASCIIDOC_EXTRA += -awith-breaking-changes
+endif
+
ASCIIDOC_DEPS += docinfo.html
SHELL_PATH ?= $(SHELL)
@@ -505,7 +510,7 @@ lint-docs-meson:
awk "/^manpages = {$$/ {flag=1 ; next } /^}$$/ { flag=0 } flag { gsub(/^ \047/, \"\"); gsub(/\047 : [157],\$$/, \"\"); print }" meson.build | \
grep -v -e '#' -e '^$$' | \
sort >tmp-meson-diff/meson.adoc && \
- ls git*.adoc scalar.adoc | grep -v -e git-bisect-lk2009.adoc -e git-tools.adoc >tmp-meson-diff/actual.adoc && \
+ ls git*.adoc scalar.adoc | grep -v -e git-bisect-lk2009.adoc -e git-pack-redundant.adoc -e git-tools.adoc >tmp-meson-diff/actual.adoc && \
if ! cmp tmp-meson-diff/meson.adoc tmp-meson-diff/actual.adoc; then \
echo "Meson man pages differ from actual man pages:"; \
diff -u tmp-meson-diff/meson.adoc tmp-meson-diff/actual.adoc; \
diff --git a/Documentation/MyFirstContribution.adoc b/Documentation/MyFirstContribution.adoc
index e41654c00a..ca1d688c9b 100644
--- a/Documentation/MyFirstContribution.adoc
+++ b/Documentation/MyFirstContribution.adoc
@@ -21,7 +21,7 @@ This tutorial aims to summarize the following documents, but the reader may find
useful additional context:
- `Documentation/SubmittingPatches`
-- `Documentation/howto/new-command.txt`
+- `Documentation/howto/new-command.adoc`
[[getting-help]]
=== Getting Help
@@ -331,7 +331,7 @@ function body:
apply standard precedence rules. `git_config_get_string_tmp()` will look up
a specific key ("user.name") and give you the value. There are a number of
single-key lookup functions like this one; you can see them all (and more info
-about how to use `git_config()`) in `Documentation/technical/api-config.txt`.
+about how to use `git_config()`) in `Documentation/technical/api-config.adoc`.
You should see that the name printed matches the one you see when you run:
@@ -367,6 +367,7 @@ But as we drill down, we can find that `status_init_config()` wraps a call
to `git_config()`. Let's modify the code we wrote in the previous commit.
Be sure to include the header to allow you to use `struct wt_status`:
+
----
#include "wt-status.h"
----
@@ -461,10 +462,10 @@ $ ./bin-wrappers/git help psuh
Your new command is undocumented! Let's fix that.
-Take a look at `Documentation/git-*.txt`. These are the manpages for the
+Take a look at `Documentation/git-*.adoc`. These are the manpages for the
subcommands that Git knows about. You can open these up and take a look to get
acquainted with the format, but then go ahead and make a new file
-`Documentation/git-psuh.txt`. Like with most of the documentation in the Git
+`Documentation/git-psuh.adoc`. Like with most of the documentation in the Git
project, help pages are written with AsciiDoc (see CodingGuidelines, "Writing
Documentation" section). Use the following template to fill out your own
manpage:
@@ -543,7 +544,7 @@ Try and run `./bin-wrappers/git psuh -h`. Your command should crash at the end.
That's because `-h` is a special case which your command should handle by
printing usage.
-Take a look at `Documentation/technical/api-parse-options.txt`. This is a handy
+Take a look at `Documentation/technical/api-parse-options.adoc`. This is a handy
tool for pulling out options you need to be able to handle, and it takes a
usage string.
@@ -1088,14 +1089,14 @@ This gives reviewers a summary of what they're in for when reviewing your topic.
The one generated for `psuh` from the sample implementation looks like this:
----
- Documentation/git-psuh.txt | 40 +++++++++++++++++++++
- Makefile | 1 +
- builtin.h | 1 +
- builtin/psuh.c | 73 ++++++++++++++++++++++++++++++++++++++
- git.c | 1 +
- t/t9999-psuh-tutorial.sh | 12 +++++++
+ Documentation/git-psuh.adoc | 40 +++++++++++++++++++++
+ Makefile | 1 +
+ builtin.h | 1 +
+ builtin/psuh.c | 73 ++++++++++++++++++++++++++++++++++++++
+ git.c | 1 +
+ t/t9999-psuh-tutorial.sh | 12 +++++++
6 files changed, 128 insertions(+)
- create mode 100644 Documentation/git-psuh.txt
+ create mode 100644 Documentation/git-psuh.adoc
create mode 100644 builtin/psuh.c
create mode 100755 t/t9999-psuh-tutorial.sh
----
diff --git a/Documentation/MyFirstObjectWalk.adoc b/Documentation/MyFirstObjectWalk.adoc
index dec8afe5b1..bfe8f5f561 100644
--- a/Documentation/MyFirstObjectWalk.adoc
+++ b/Documentation/MyFirstObjectWalk.adoc
@@ -15,7 +15,7 @@ revision walk is used for operations like `git log`.
=== Related Reading
-- `Documentation/user-manual.txt` under "Hacking Git" contains some coverage of
+- `Documentation/user-manual.adoc` under "Hacking Git" contains some coverage of
the revision walker in its various incarnations.
- `revision.h`
- https://eagain.net/articles/git-for-computer-scientists/[Git for Computer Scientists]
@@ -112,7 +112,7 @@ $ GIT_TRACE=1 ./bin-wrappers/git walken
----
NOTE: For a more exhaustive overview of the new command process, take a look at
-`Documentation/MyFirstContribution.txt`.
+`Documentation/MyFirstContribution.adoc`.
NOTE: A reference implementation can be found at
https://github.com/nasamuffin/git/tree/revwalk.
@@ -132,7 +132,7 @@ used to track the allocated size of the list.
Per entry, we find:
`item` is the object provided upon which to base the object walk. Items in Git
-can be blobs, trees, commits, or tags. (See `Documentation/gittutorial-2.txt`.)
+can be blobs, trees, commits, or tags. (See `Documentation/gittutorial-2.adoc`.)
`name` is the object ID (OID) of the object - a hex string you may be familiar
with from using Git to organize your source in the past. Check the tutorial
@@ -141,7 +141,7 @@ from.
`whence` indicates some information about what to do with the parents of the
specified object. We'll explore this flag more later on; take a look at
-`Documentation/revisions.txt` to get an idea of what could set the `whence`
+`Documentation/revisions.adoc` to get an idea of what could set the `whence`
value.
`flags` are used to hint the beginning of the revision walk and are the first
@@ -153,7 +153,7 @@ can be used during the walk, as well.
This one is quite a bit longer, and many fields are only used during the walk
by `revision.c` - not configuration options. Most of the configurable flags in
-`struct rev_info` have a mirror in `Documentation/rev-list-options.txt`. It's a
+`struct rev_info` have a mirror in `Documentation/rev-list-options.adoc`. It's a
good idea to take some time and read through that document.
== Basic Commit Walk
@@ -287,6 +287,7 @@ static void final_rev_info_setup(struct rev_info *rev)
====
Instead of using the shorthand `add_head_to_pending()`, you could do
something like this:
+
----
struct setup_revision_opt opt;
@@ -295,6 +296,7 @@ something like this:
opt.revarg_opt = REVARG_COMMITTISH;
setup_revisions(argc, argv, rev, &opt);
----
+
Using a `setup_revision_opt` gives you finer control over your walk's starting
point.
====
@@ -710,7 +712,7 @@ objects grows along with the Git project.
=== Adding a Filter
There are a handful of filters that we can apply to the object walk laid out in
-`Documentation/rev-list-options.txt`. These filters are typically useful for
+`Documentation/rev-list-options.adoc`. These filters are typically useful for
operations such as creating packfiles or performing a partial clone. They are
defined in `list-objects-filter-options.h`. For the purposes of this tutorial we
will use the "tree:1" filter, which causes the walk to omit all trees and blobs
diff --git a/Documentation/RelNotes/2.49.0.adoc b/Documentation/RelNotes/2.49.0.adoc
index 3d253455d3..494c83096f 100644
--- a/Documentation/RelNotes/2.49.0.adoc
+++ b/Documentation/RelNotes/2.49.0.adoc
@@ -93,6 +93,18 @@ Performance, Internal Implementation, Development Support etc.
* Rename processing in the recursive merge backend has seen a micro
optimization.
+ * The path.[ch] API takes an explicit repository parameter passed
+ throughout the callchain, instead of relying on the_repository
+ singleton instance.
+
+ * Large-object promisor protocol extension has been introduced.
+
+ * The editorconfig file is updated to tell us that bash scripts are
+ similar to general Bourne shell scripts.
+
+ * Meson-based build procedure forgot to build some docs, which has
+ been corrected.
+
Fixes since v2.48
-----------------
@@ -272,3 +284,5 @@ Fixes since v2.48
(merge 45761988ac en/doc-renormalize later to maint).
(merge 832f56f06a jc/doc-boolean-synonyms later to maint).
(merge 3eeed876a9 ac/doc-http-ssl-type-config later to maint).
+ (merge c268e3285d jc/breaking-changes-early-adopter-option later to maint).
+ (merge 0d03fda6a5 pb/doc-follow-remote-head later to maint).
diff --git a/Documentation/RelNotes/2.50.0.adoc b/Documentation/RelNotes/2.50.0.adoc
new file mode 100644
index 0000000000..b787dce3da
--- /dev/null
+++ b/Documentation/RelNotes/2.50.0.adoc
@@ -0,0 +1,126 @@
+Git v2.50 Release Notes
+=======================
+
+UI, Workflows & Features
+------------------------
+
+ * A post-processing filter for "diff --raw" output has been
+ introduced.
+
+ * "git repack" learned "--combine-cruft-below-size" option that
+ controls how cruft-packs are combined.
+
+ * TCP keepalive behaviour on http transports can now be configured by
+ calling cURL library.
+
+ * Incrementally updating multi-pack index files.
+
+
+Performance, Internal Implementation, Development Support etc.
+--------------------------------------------------------------
+
+ * A handful of built-in command implementations have been rewritten
+ to use the repository instance supplied by git.c:run_builtin(), its
+ caller.
+
+ * "git fsck" becomes more careful when checking the refs.
+
+ * "git fast-export | git fast-import" learns to deal with commit and
+ tag objects with embedded signatures a bit better.
+
+ * The code paths to check whether a refname X is available (by seeing
+ if another ref X/Y exists, etc.) have been optimized.
+
+ * First step of deprecating and removing merge-recursive.
+
+ * In protocol v2 where the refs advertisement is constrained, we try
+ to tell the server side not to limit the advertisement when there
+ is no specific need to, which has been the source of confusion and
+ recent bugs. Revamp the logic to simplify.
+
+ * Update meson based build procedure for breaking changes support.
+
+ * Enable -Wunreachable-code for developer builds.
+
+ * Ensure what we write in assert() does not have side effects,
+ and introduce ASSERT() macro to mark those that cannot be
+ mechanically checked for lack of side effects.
+
+ * Give more meaningful error return values from block writer layer of
+ the reftable ref-API backend.
+
+ * Make the code in reftable library less reliant on the service
+ routines it used to borrow from Git proper, to make it easier to
+ use by external users of the library.
+
+ * CI update.
+
+
+Fixes since v2.49
+-----------------
+
+ * The refname exclusion logic in the packed-ref backend has been
+ broken for some time, which confused upload-pack to advertise
+ different set of refs. This has been corrected.
+ (merge 10e8a9352b tb/refs-exclude-fixes later to maint).
+
+ * The merge-recursive and merge-ort machinery crashed in corner cases
+ when certain renames are involved.
+ (merge 3adba40858 en/merge-process-renames-crash-fix later to maint).
+
+ * Certain "cruft" objects would have never been refreshed when there
+ are multiple cruft packs in the repository, which has been
+ corrected.
+ (merge 08f612ba70 tb/multi-cruft-pack-refresh-fix later to maint).
+
+ * The xdiff code on 32-bit platform misbehaved when an insanely large
+ context size is given, which has been corrected.
+ (merge d39e28e68c rs/xdiff-context-length-fix later to maint).
+
+ * GitHub Actions CI switched on a CI/CD variable that does not exist
+ when choosing what packages to install etc., which has been
+ corrected.
+ (merge ee89f7c79d kn/ci-meson-check-build-docs-fix later to maint).
+
+ * Using "git name-rev --stdin" as an example, improve the framework to
+ prepare tests to pretend to be in the future where the breaking
+ changes have already happened.
+ (merge de3dec1187 jc/name-rev-stdin later to maint).
+
+ * An earlier code refactoring of the hash machinery missed a few
+ required calls to init_fn.
+ (merge d39f04b638 jh/hash-init-fixes later to maint).
+
+ * A documentation page was left out from formatting and installation,
+ which has been corrected.
+ (merge ae85116f18 pw/build-breaking-changes-doc later to maint).
+
+ * The bash command line completion script (in contrib/) has been
+ updated to cope with remote repository nicknames with slashes in
+ them.
+ (merge 778d2f1760 dm/completion-remote-names-fix later to maint).
+
+ * "Dubious ownership" checks on Windows has been tightened up.
+ (merge 5bb88e89ef js/mingw-admins-are-special later to maint).
+
+ * Layout configuration in vimdiff backend didn't work as advertised,
+ which has been corrected.
+ (merge 93bab2d04b fr/vimdiff-layout-fixes later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+ (merge 227c4f33a0 ja/doc-block-delimiter-markup-fix later to maint).
+ (merge 2bfd3b3685 ab/decorate-code-cleanup later to maint).
+ (merge 5337daddc7 am/dir-dedup-decl-of-repository later to maint).
+ (merge 554051d691 en/diff-rename-follow-fix later to maint).
+ (merge a18c18b470 en/random-cleanups later to maint).
+ (merge 5af21c9acb hj/doc-rev-list-ancestry-fix later to maint).
+ (merge 26d76ca284 aj/doc-restore-p-update later to maint).
+ (merge 2c0dcb9754 cc/lop-remote later to maint).
+ (merge 7b399322a2 ja/doc-branch-markup later to maint).
+ (merge ee434e1807 pw/doc-pack-refs-markup-fix later to maint).
+ (merge c000918eb7 tb/bitamp-typofix later to maint).
+ (merge fa8cd29676 js/imap-send-peer-cert-verify later to maint).
+ (merge 98b423bc1c rs/clear-commit-marks-simplify later to maint).
+ (merge 133d065dd6 ta/bulk-checkin-signed-compare-false-warning-fix later to maint).
+ (merge d2827dc31e es/meson-build-skip-coccinelle later to maint).
+ (merge ee8edb7156 dk/vimdiff-doc-fix later to maint).
diff --git a/Documentation/ToolsForGit.adoc b/Documentation/ToolsForGit.adoc
index ae7690b45d..a842c13327 100644
--- a/Documentation/ToolsForGit.adoc
+++ b/Documentation/ToolsForGit.adoc
@@ -34,6 +34,7 @@ This is adapted from Linux's suggestion in its CodingStyle document:
- To follow the rules in CodingGuidelines, it's useful to put the following in
GIT_CHECKOUT/.dir-locals.el, assuming you use cperl-mode:
+
----
;; note the first part is useful for C editing, too
((nil . ((indent-tabs-mode . t)
diff --git a/Documentation/build-docdep.perl b/Documentation/build-docdep.perl
index 315efaa2fa..781da12b2e 100755
--- a/Documentation/build-docdep.perl
+++ b/Documentation/build-docdep.perl
@@ -4,15 +4,15 @@ my ($build_dir) = @ARGV;
my %include = ();
my %included = ();
-for my $text (<*.txt>) {
- open I, '<', $text || die "cannot read: $text";
+for my $adoc (<*.adoc>) {
+ open I, '<', $adoc || die "cannot read: $adoc";
while (<I>) {
if (/^include::/) {
chomp;
s/^include::\s*//;
s/\[\]//;
s/{build_dir}/${build_dir}/;
- $include{$text}{$_} = 1;
+ $include{$adoc}{$_} = 1;
$included{$_} = 1;
}
}
@@ -23,14 +23,14 @@ for my $text (<*.txt>) {
my $changed = 1;
while ($changed) {
$changed = 0;
- while (my ($text, $included) = each %include) {
+ while (my ($adoc, $included) = each %include) {
for my $i (keys %$included) {
- # $text has include::$i; if $i includes $j
- # $text indirectly includes $j.
+ # $adoc has include::$i; if $i includes $j
+ # $adoc indirectly includes $j.
if (exists $include{$i}) {
for my $j (keys %{$include{$i}}) {
- if (!exists $include{$text}{$j}) {
- $include{$text}{$j} = 1;
+ if (!exists $include{$adoc}{$j}) {
+ $include{$adoc}{$j} = 1;
$included{$j} = 1;
$changed = 1;
}
@@ -40,10 +40,10 @@ while ($changed) {
}
}
-foreach my $text (sort keys %include) {
- my $included = $include{$text};
- if (! exists $included{$text} &&
- (my $base = $text) =~ s/\.txt$//) {
+foreach my $adoc (sort keys %include) {
+ my $included = $include{$adoc};
+ if (! exists $included{$adoc} &&
+ (my $base = $adoc) =~ s/\.adoc$//) {
print "$base.html $base.xml : ", join(" ", sort keys %$included), "\n";
}
}
diff --git a/Documentation/config/branch.adoc b/Documentation/config/branch.adoc
index 432b9cd2c0..e35ea7ac64 100644
--- a/Documentation/config/branch.adoc
+++ b/Documentation/config/branch.adoc
@@ -1,41 +1,42 @@
-branch.autoSetupMerge::
- Tells 'git branch', 'git switch' and 'git checkout' to set up new branches
+`branch.autoSetupMerge`::
+ Tells `git branch`, `git switch` and `git checkout` to set up new branches
so that linkgit:git-pull[1] will appropriately merge from the
starting point branch. Note that even if this option is not set,
this behavior can be chosen per-branch using the `--track`
- and `--no-track` options. The valid settings are: `false` -- no
- automatic setup is done; `true` -- automatic setup is done when the
- starting point is a remote-tracking branch; `always` --
- automatic setup is done when the starting point is either a
- local branch or remote-tracking branch; `inherit` -- if the starting point
- has a tracking configuration, it is copied to the new
- branch; `simple` -- automatic setup is done only when the starting point
+ and `--no-track` options. This option defaults to `true`. The valid settings
+ are:
+`false`;; no automatic setup is done
+`true`;; automatic setup is done when the starting point is a remote-tracking branch
+`always`;; automatic setup is done when the starting point is either a
+ local branch or remote-tracking branch
+`inherit`;; if the starting point has a tracking configuration, it is copied to the new
+ branch
+`simple`;; automatic setup is done only when the starting point
is a remote-tracking branch and the new branch has the same name as the
- remote branch. This option defaults to true.
+ remote branch.
-branch.autoSetupRebase::
- When a new branch is created with 'git branch', 'git switch' or 'git checkout'
+`branch.autoSetupRebase`::
+ When a new branch is created with `git branch`, `git switch` or `git checkout`
that tracks another branch, this variable tells Git to set
- up pull to rebase instead of merge (see "branch.<name>.rebase").
- When `never`, rebase is never automatically set to true.
- When `local`, rebase is set to true for tracked branches of
- other local branches.
- When `remote`, rebase is set to true for tracked branches of
- remote-tracking branches.
- When `always`, rebase will be set to true for all tracking
- branches.
- See "branch.autoSetupMerge" for details on how to set up a
- branch to track another branch.
- This option defaults to never.
+ up pull to rebase instead of merge (see `branch.<name>.rebase`).
+ The valid settings are:
+`never`;; rebase is never automatically set to true.
+`local`;; rebase is set to true for tracked branches of other local branches.
+`remote`;; rebase is set to true for tracked branches of remote-tracking branches.
+`always`;; rebase will be set to true for all tracking branches.
-branch.sort::
++
+See `branch.autoSetupMerge` for details on how to set up a branch to track another branch.
+This option defaults to `never`.
+
+`branch.sort`::
This variable controls the sort ordering of branches when displayed by
- linkgit:git-branch[1]. Without the "--sort=<value>" option provided, the
+ linkgit:git-branch[1]. Without the `--sort=<value>` option provided, the
value of this variable will be used as the default.
See linkgit:git-for-each-ref[1] field names for valid values.
-branch.<name>.remote::
- When on branch <name>, it tells 'git fetch' and 'git push'
+`branch.<name>.remote`::
+ When on branch _<name>_, it tells `git fetch` and `git push`
which remote to fetch from or push to. The remote to push to
may be overridden with `remote.pushDefault` (for all branches).
The remote to push to, for the current branch, may be further
@@ -46,58 +47,58 @@ branch.<name>.remote::
Additionally, `.` (a period) is the current local repository
(a dot-repository), see `branch.<name>.merge`'s final note below.
-branch.<name>.pushRemote::
- When on branch <name>, it overrides `branch.<name>.remote` for
+`branch.<name>.pushRemote`::
+ When on branch _<name>_, it overrides `branch.<name>.remote` for
pushing. It also overrides `remote.pushDefault` for pushing
- from branch <name>. When you pull from one place (e.g. your
+ from branch _<name>_. When you pull from one place (e.g. your
upstream) and push to another place (e.g. your own publishing
repository), you would want to set `remote.pushDefault` to
specify the remote to push to for all branches, and use this
option to override it for a specific branch.
-branch.<name>.merge::
- Defines, together with branch.<name>.remote, the upstream branch
- for the given branch. It tells 'git fetch'/'git pull'/'git rebase' which
- branch to merge and can also affect 'git push' (see push.default).
- When in branch <name>, it tells 'git fetch' the default
- refspec to be marked for merging in FETCH_HEAD. The value is
+`branch.<name>.merge`::
+ Defines, together with `branch.<name>.remote`, the upstream branch
+ for the given branch. It tells `git fetch`/`git pull`/`git rebase` which
+ branch to merge and can also affect `git push` (see `push.default`).
+ When in branch _<name>_, it tells `git fetch` the default
+ refspec to be marked for merging in `FETCH_HEAD`. The value is
handled like the remote part of a refspec, and must match a
ref which is fetched from the remote given by
- "branch.<name>.remote".
- The merge information is used by 'git pull' (which first calls
- 'git fetch') to lookup the default branch for merging. Without
- this option, 'git pull' defaults to merge the first refspec fetched.
+ `branch.<name>.remote`.
+ The merge information is used by `git pull` (which first calls
+ `git fetch`) to lookup the default branch for merging. Without
+ this option, `git pull` defaults to merge the first refspec fetched.
Specify multiple values to get an octopus merge.
- If you wish to setup 'git pull' so that it merges into <name> from
+ If you wish to setup `git pull` so that it merges into <name> from
another branch in the local repository, you can point
branch.<name>.merge to the desired branch, and use the relative path
- setting `.` (a period) for branch.<name>.remote.
+ setting `.` (a period) for `branch.<name>.remote`.
-branch.<name>.mergeOptions::
- Sets default options for merging into branch <name>. The syntax and
+`branch.<name>.mergeOptions`::
+ Sets default options for merging into branch _<name>_. The syntax and
supported options are the same as those of linkgit:git-merge[1], but
option values containing whitespace characters are currently not
supported.
-branch.<name>.rebase::
- When true, rebase the branch <name> on top of the fetched branch,
+`branch.<name>.rebase`::
+ When true, rebase the branch _<name>_ on top of the fetched branch,
instead of merging the default branch from the default remote when
- "git pull" is run. See "pull.rebase" for doing this in a non
+ `git pull` is run. See `pull.rebase` for doing this in a non
branch-specific manner.
+
-When `merges` (or just 'm'), pass the `--rebase-merges` option to 'git rebase'
+When `merges` (or just `m`), pass the `--rebase-merges` option to `git rebase`
so that the local merge commits are included in the rebase (see
linkgit:git-rebase[1] for details).
+
-When the value is `interactive` (or just 'i'), the rebase is run in interactive
+When the value is `interactive` (or just `i`), the rebase is run in interactive
mode.
+
*NOTE*: this is a possibly dangerous operation; do *not* use
it unless you understand the implications (see linkgit:git-rebase[1]
for details).
-branch.<name>.description::
+`branch.<name>.description`::
Branch description, can be edited with
`git branch --edit-description`. Branch description is
- automatically added to the format-patch cover letter or
- request-pull summary.
+ automatically added to the `format-patch` cover letter or
+ `request-pull` summary.
diff --git a/Documentation/config/http.adoc b/Documentation/config/http.adoc
index 22a8803dea..67393282fa 100644
--- a/Documentation/config/http.adoc
+++ b/Documentation/config/http.adoc
@@ -296,6 +296,24 @@ http.lowSpeedLimit, http.lowSpeedTime::
Can be overridden by the `GIT_HTTP_LOW_SPEED_LIMIT` and
`GIT_HTTP_LOW_SPEED_TIME` environment variables.
+http.keepAliveIdle::
+ Specifies how long in seconds to wait on an idle connection
+ before sending TCP keepalive probes (if supported by the OS). If
+ unset, curl's default value is used. Can be overridden by the
+ `GIT_HTTP_KEEPALIVE_IDLE` environment variable.
+
+http.keepAliveInterval::
+ Specifies how long in seconds to wait between TCP keepalive
+ probes (if supported by the OS). If unset, curl's default value
+ is used. Can be overridden by the `GIT_HTTP_KEEPALIVE_INTERVAL`
+ environment variable.
+
+http.keepAliveCount::
+ Specifies how many TCP keepalive probes to send before giving up
+ and terminating the connection (if supported by the OS). If
+ unset, curl's default value is used. Can be overridden by the
+ `GIT_HTTP_KEEPALIVE_COUNT` environment variable.
+
http.noEPSV::
A boolean which disables using of EPSV ftp command by curl.
This can be helpful with some "poor" ftp servers which don't
diff --git a/Documentation/config/promisor.adoc b/Documentation/config/promisor.adoc
index 98c5cb2ec2..2638b01f83 100644
--- a/Documentation/config/promisor.adoc
+++ b/Documentation/config/promisor.adoc
@@ -1,3 +1,30 @@
promisor.quiet::
If set to "true" assume `--quiet` when fetching additional
objects for a partial clone.
+
+promisor.advertise::
+ If set to "true", a server will use the "promisor-remote"
+ capability, see linkgit:gitprotocol-v2[5], to advertise the
+ promisor remotes it is using, if it uses some. Default is
+ "false", which means the "promisor-remote" capability is not
+ advertised.
+
+promisor.acceptFromServer::
+ If set to "all", a client will accept all the promisor remotes
+ a server might advertise using the "promisor-remote"
+ capability. If set to "knownName" the client will accept
+ promisor remotes which are already configured on the client
+ and have the same name as those advertised by the client. This
+ is not very secure, but could be used in a corporate setup
+ where servers and clients are trusted to not switch name and
+ URLs. If set to "knownUrl", the client will accept promisor
+ remotes which have both the same name and the same URL
+ configured on the client as the name and URL advertised by the
+ server. This is more secure than "all" or "knownName", so it
+ should be used if possible instead of those options. Default
+ is "none", which means no promisor remote advertised by a
+ server will be accepted. By accepting a promisor remote, the
+ client agrees that the server might omit objects that are
+ lazily fetchable from this promisor remote from its responses
+ to "fetch" and "clone" requests from the client. Name and URL
+ comparisons are case sensitive. See linkgit:gitprotocol-v2[5].
diff --git a/Documentation/config/remote.adoc b/Documentation/config/remote.adoc
index 4118c219c1..25fe219d10 100644
--- a/Documentation/config/remote.adoc
+++ b/Documentation/config/remote.adoc
@@ -101,21 +101,21 @@ remote.<name>.serverOption::
The default set of server options used when fetching from this remote.
These server options can be overridden by the `--server-option=` command
line arguments.
++
+This is a multi-valued variable, and an empty value can be used in a higher
+priority configuration file (e.g. `.git/config` in a repository) to clear
+the values inherited from a lower priority configuration files (e.g.
+`$HOME/.gitconfig`).
remote.<name>.followRemoteHEAD::
How linkgit:git-fetch[1] should handle updates to `remotes/<name>/HEAD`.
The default value is "create", which will create `remotes/<name>/HEAD`
- if it exists on the remote, but not locally, but will not touch an
- already existing local reference. Setting to "warn" will print
- a message if the remote has a different value, than the local one and
+ if it exists on the remote, but not locally; this will not touch an
+ already existing local reference. Setting it to "warn" will print
+ a message if the remote has a different value than the local one;
in case there is no local reference, it behaves like "create".
A variant on "warn" is "warn-if-not-$branch", which behaves like
"warn", but if `HEAD` on the remote is `$branch` it will be silent.
- Setting to "always" will silently update it to the value on the remote.
- Finally, setting it to "never" will never change or create the local
- reference.
-+
-This is a multi-valued variable, and an empty value can be used in a higher
-priority configuration file (e.g. `.git/config` in a repository) to clear
-the values inherited from a lower priority configuration files (e.g.
-`$HOME/.gitconfig`).
+ Setting it to "always" will silently update `remotes/<name>/HEAD` to
+ the value on the remote. Finally, setting it to "never" will never
+ change or create the local reference.
diff --git a/Documentation/fsck-msgids.adoc b/Documentation/fsck-msgids.adoc
index b14bc44ca4..9601fff228 100644
--- a/Documentation/fsck-msgids.adoc
+++ b/Documentation/fsck-msgids.adoc
@@ -16,6 +16,13 @@
`badObjectSha1`::
(ERROR) An object has a bad sha1.
+`badPackedRefEntry`::
+ (ERROR) The "packed-refs" file contains an invalid entry.
+
+`badPackedRefHeader`::
+ (ERROR) The "packed-refs" file contains an invalid
+ header.
+
`badParentSha1`::
(ERROR) A commit object has a bad parent sha1.
@@ -176,6 +183,13 @@
`nullSha1`::
(WARN) Tree contains entries pointing to a null sha1.
+`packedRefEntryNotTerminated`::
+ (ERROR) The "packed-refs" file contains an entry that is
+ not terminated by a newline.
+
+`packedRefUnsorted`::
+ (ERROR) The "packed-refs" file is not sorted.
+
`refMissingNewline`::
(INFO) A loose ref that does not end with newline(LF). As
valid implementations of Git never created such a loose ref
diff --git a/Documentation/git-bisect.adoc b/Documentation/git-bisect.adoc
index 82f944dc03..58dbb74a15 100644
--- a/Documentation/git-bisect.adoc
+++ b/Documentation/git-bisect.adoc
@@ -495,6 +495,7 @@ $ git bisect old HEAD~10 # the tenth commit from now is marked as old
------------
+
or:
++
------------
$ git bisect start --term-old broken --term-new fixed
$ git bisect fixed
diff --git a/Documentation/git-branch.adoc b/Documentation/git-branch.adoc
index 7a073a36d6..50a1e13e1f 100644
--- a/Documentation/git-branch.adoc
+++ b/Documentation/git-branch.adoc
@@ -7,23 +7,23 @@ git-branch - List, create, or delete branches
SYNOPSIS
--------
-[verse]
-'git branch' [--color[=<when>] | --no-color] [--show-current]
- [-v [--abbrev=<n> | --no-abbrev]]
- [--column[=<options>] | --no-column] [--sort=<key>]
- [--merged [<commit>]] [--no-merged [<commit>]]
- [--contains [<commit>]] [--no-contains [<commit>]]
- [--points-at <object>] [--format=<format>]
- [(-r | --remotes) | (-a | --all)]
- [--list] [<pattern>...]
-'git branch' [--track[=(direct|inherit)] | --no-track] [-f]
- [--recurse-submodules] <branchname> [<start-point>]
-'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
-'git branch' --unset-upstream [<branchname>]
-'git branch' (-m | -M) [<oldbranch>] <newbranch>
-'git branch' (-c | -C) [<oldbranch>] <newbranch>
-'git branch' (-d | -D) [-r] <branchname>...
-'git branch' --edit-description [<branchname>]
+[synopsis]
+git branch [--color[=<when>] | --no-color] [--show-current]
+ [-v [--abbrev=<n> | --no-abbrev]]
+ [--column[=<options>] | --no-column] [--sort=<key>]
+ [--merged [<commit>]] [--no-merged [<commit>]]
+ [--contains [<commit>]] [--no-contains [<commit>]]
+ [--points-at <object>] [--format=<format>]
+ [(-r|--remotes) | (-a|--all)]
+ [--list] [<pattern>...]
+git branch [--track[=(direct|inherit)] | --no-track] [-f]
+ [--recurse-submodules] <branch-name> [<start-point>]
+git branch (--set-upstream-to=<upstream>|-u <upstream>) [<branch-name>]
+git branch --unset-upstream [<branch-name>]
+git branch (-m|-M) [<old-branch>] <new-branch>
+git branch (-c|-C) [<old-branch>] <new-branch>
+git branch (-d|-D) [-r] <branch-name>...
+git branch --edit-description [<branch-name>]
DESCRIPTION
-----------
@@ -49,173 +49,184 @@ With `--contains`, shows only the branches that contain the named commit
named commit), `--no-contains` inverts it. With `--merged`, only branches
merged into the named commit (i.e. the branches whose tip commits are
reachable from the named commit) will be listed. With `--no-merged` only
-branches not merged into the named commit will be listed. If the <commit>
+branches not merged into the named commit will be listed. If the _<commit>_
argument is missing it defaults to `HEAD` (i.e. the tip of the current
branch).
-The command's second form creates a new branch head named <branchname>
-which points to the current `HEAD`, or <start-point> if given. As a
-special case, for <start-point>, you may use `"A...B"` as a shortcut for
-the merge base of `A` and `B` if there is exactly one merge base. You
-can leave out at most one of `A` and `B`, in which case it defaults to
-`HEAD`.
+The command's second form creates a new branch head named _<branch-name>_
+which points to the current `HEAD`, or _<start-point>_ if given. As a
+special case, for _<start-point>_, you may use `<rev-A>...<rev-B>` as a
+shortcut for the merge base of _<rev-A>_ and _<rev-B>_ if there is exactly
+one merge base. You can leave out at most one of _<rev-A>_ and _<rev-B>_,
+in which case it defaults to `HEAD`.
Note that this will create the new branch, but it will not switch the
-working tree to it; use "git switch <newbranch>" to switch to the
+working tree to it; use `git switch <new-branch>` to switch to the
new branch.
When a local branch is started off a remote-tracking branch, Git sets up the
branch (specifically the `branch.<name>.remote` and `branch.<name>.merge`
-configuration entries) so that 'git pull' will appropriately merge from
+configuration entries) so that `git pull` will appropriately merge from
the remote-tracking branch. This behavior may be changed via the global
`branch.autoSetupMerge` configuration flag. That setting can be
overridden by using the `--track` and `--no-track` options, and
changed later using `git branch --set-upstream-to`.
-With a `-m` or `-M` option, <oldbranch> will be renamed to <newbranch>.
-If <oldbranch> had a corresponding reflog, it is renamed to match
-<newbranch>, and a reflog entry is created to remember the branch
-renaming. If <newbranch> exists, -M must be used to force the rename
+With a `-m` or `-M` option, _<old-branch>_ will be renamed to _<new-branch>_.
+If _<old-branch>_ had a corresponding reflog, it is renamed to match
+_<new-branch>_, and a reflog entry is created to remember the branch
+renaming. If _<new-branch>_ exists, `-M` must be used to force the rename
to happen.
The `-c` and `-C` options have the exact same semantics as `-m` and
`-M`, except instead of the branch being renamed, it will be copied to a
new name, along with its config and reflog.
-With a `-d` or `-D` option, `<branchname>` will be deleted. You may
+With a `-d` or `-D` option, _<branch-name>_ will be deleted. You may
specify more than one branch for deletion. If the branch currently
has a reflog then the reflog will also be deleted.
Use `-r` together with `-d` to delete remote-tracking branches. Note, that it
only makes sense to delete remote-tracking branches if they no longer exist
-in the remote repository or if 'git fetch' was configured not to fetch
-them again. See also the 'prune' subcommand of linkgit:git-remote[1] for a
+in the remote repository or if `git fetch` was configured not to fetch
+them again. See also the `prune` subcommand of linkgit:git-remote[1] for a
way to clean up all obsolete remote-tracking branches.
OPTIONS
-------
--d::
---delete::
+`-d`::
+`--delete`::
Delete a branch. The branch must be fully merged in its
upstream branch, or in `HEAD` if no upstream was set with
`--track` or `--set-upstream-to`.
--D::
+`-D`::
Shortcut for `--delete --force`.
---create-reflog::
+`--create-reflog`::
Create the branch's reflog. This activates recording of
all changes made to the branch ref, enabling use of date
- based sha1 expressions such as "<branchname>@\{yesterday}".
+ based sha1 expressions such as `<branch-name>@{yesterday}`.
Note that in non-bare repositories, reflogs are usually
enabled by default by the `core.logAllRefUpdates` config option.
The negated form `--no-create-reflog` only overrides an earlier
`--create-reflog`, but currently does not negate the setting of
`core.logAllRefUpdates`.
--f::
---force::
- Reset <branchname> to <start-point>, even if <branchname> exists
- already. Without `-f`, 'git branch' refuses to change an existing branch.
+`-f`::
+`--force`::
+ Reset _<branch-name>_ to _<start-point>_, even if _<branch-name>_ exists
+ already. Without `-f`, `git branch` refuses to change an existing branch.
In combination with `-d` (or `--delete`), allow deleting the
branch irrespective of its merged status, or whether it even
points to a valid commit. In combination with
`-m` (or `--move`), allow renaming the branch even if the new
branch name already exists, the same applies for `-c` (or `--copy`).
+
-Note that 'git branch -f <branchname> [<start-point>]', even with '-f',
-refuses to change an existing branch `<branchname>` that is checked out
+Note that `git branch -f <branch-name> [<start-point>]`, even with `-f`,
+refuses to change an existing branch _<branch-name>_ that is checked out
in another worktree linked to the same repository.
--m::
---move::
+`-m`::
+`--move`::
Move/rename a branch, together with its config and reflog.
--M::
+`-M`::
Shortcut for `--move --force`.
--c::
---copy::
+`-c`::
+`--copy`::
Copy a branch, together with its config and reflog.
--C::
+`-C`::
Shortcut for `--copy --force`.
---color[=<when>]::
+`--color[=<when>]`::
Color branches to highlight current, local, and
remote-tracking branches.
- The value must be always (the default), never, or auto.
+ The value must be `always` (the default), `never`, or `auto`.
---no-color::
+`--no-color`::
Turn off branch colors, even when the configuration file gives the
default to color output.
Same as `--color=never`.
--i::
---ignore-case::
+`-i`::
+`--ignore-case`::
Sorting and filtering branches are case insensitive.
---omit-empty::
+`--omit-empty`::
Do not print a newline after formatted refs where the format expands
to the empty string.
---column[=<options>]::
---no-column::
+`--column[=<options>]`::
+`--no-column`::
Display branch listing in columns. See configuration variable
`column.branch` for option syntax. `--column` and `--no-column`
- without options are equivalent to 'always' and 'never' respectively.
+ without options are equivalent to `always` and `never` respectively.
+
This option is only applicable in non-verbose mode.
--r::
---remotes::
- List or delete (if used with -d) the remote-tracking branches.
+`--sort=<key>`::
+ Sort based on _<key>_. Prefix `-` to sort in descending
+ order of the value. You may use the `--sort=<key>` option
+ multiple times, in which case the last key becomes the primary
+ key. The keys supported are the same as those in linkgit:git-for-each-ref[1].
+ Sort order defaults to the value configured for the
+ `branch.sort` variable if it exists, or to sorting based on the
+ full refname (including `refs/...` prefix). This lists
+ detached `HEAD` (if present) first, then local branches and
+ finally remote-tracking branches. See linkgit:git-config[1].
+
+`-r`::
+`--remotes`::
+ List or delete (if used with `-d`) the remote-tracking branches.
Combine with `--list` to match the optional pattern(s).
--a::
---all::
+`-a`::
+`--all`::
List both remote-tracking branches and local branches.
Combine with `--list` to match optional pattern(s).
--l::
---list::
+`-l`::
+`--list`::
List branches. With optional `<pattern>...`, e.g. `git
branch --list 'maint-*'`, list only the branches that match
the pattern(s).
---show-current::
- Print the name of the current branch. In detached HEAD state,
+`--show-current`::
+ Print the name of the current branch. In detached `HEAD` state,
nothing is printed.
--v::
--vv::
---verbose::
+`-v`::
+`-vv`::
+`--verbose`::
When in list mode,
show sha1 and commit subject line for each head, along with
relationship to upstream branch (if any). If given twice, print
the path of the linked worktree (if any) and the name of the upstream
branch, as well (see also `git remote show <remote>`). Note that the
- current worktree's HEAD will not have its path printed (it will always
+ current worktree's `HEAD` will not have its path printed (it will always
be your current directory).
--q::
---quiet::
+`-q`::
+`--quiet`::
Be more quiet when creating or deleting a branch, suppressing
non-error messages.
---abbrev=<n>::
+`--abbrev=<n>`::
In the verbose listing that show the commit object name,
- show the shortest prefix that is at least '<n>' hexdigits
+ show the shortest prefix that is at least _<n>_ hexdigits
long that uniquely refers the object.
The default value is 7 and can be overridden by the `core.abbrev`
config option.
---no-abbrev::
+`--no-abbrev`::
Display the full sha1s in the output listing rather than abbreviating them.
--t::
---track[=(direct|inherit)]::
+`-t`::
+`--track[=(direct|inherit)]`::
When creating a new branch, set up `branch.<name>.remote` and
`branch.<name>.merge` configuration entries to set "upstream" tracking
configuration for the new branch. This
@@ -229,7 +240,7 @@ The exact upstream branch is chosen depending on the optional argument:
itself as the upstream; `--track=inherit` means to copy the upstream
configuration of the start-point branch.
+
-The branch.autoSetupMerge configuration variable specifies how `git switch`,
+The `branch.autoSetupMerge` configuration variable specifies how `git switch`,
`git checkout` and `git branch` should behave when neither `--track` nor
`--no-track` are specified:
+
@@ -238,106 +249,94 @@ were given whenever the start-point is a remote-tracking branch.
`false` behaves as if `--no-track` were given. `always` behaves as though
`--track=direct` were given. `inherit` behaves as though `--track=inherit`
were given. `simple` behaves as though `--track=direct` were given only when
-the start-point is a remote-tracking branch and the new branch has the same
+the _<start-point>_ is a remote-tracking branch and the new branch has the same
name as the remote branch.
+
See linkgit:git-pull[1] and linkgit:git-config[1] for additional discussion on
how the `branch.<name>.remote` and `branch.<name>.merge` options are used.
---no-track::
+`--no-track`::
Do not set up "upstream" configuration, even if the
- branch.autoSetupMerge configuration variable is set.
+ `branch.autoSetupMerge` configuration variable is set.
---recurse-submodules::
- THIS OPTION IS EXPERIMENTAL! Causes the current command to
+`--recurse-submodules`::
+ THIS OPTION IS EXPERIMENTAL! Cause the current command to
recurse into submodules if `submodule.propagateBranches` is
enabled. See `submodule.propagateBranches` in
linkgit:git-config[1]. Currently, only branch creation is
supported.
+
-When used in branch creation, a new branch <branchname> will be created
+When used in branch creation, a new branch _<branch-name>_ will be created
in the superproject and all of the submodules in the superproject's
-<start-point>. In submodules, the branch will point to the submodule
-commit in the superproject's <start-point> but the branch's tracking
+_<start-point>_. In submodules, the branch will point to the submodule
+commit in the superproject's _<start-point>_ but the branch's tracking
information will be set up based on the submodule's branches and remotes
e.g. `git branch --recurse-submodules topic origin/main` will create the
submodule branch "topic" that points to the submodule commit in the
superproject's "origin/main", but tracks the submodule's "origin/main".
---set-upstream::
+`--set-upstream`::
As this option had confusing syntax, it is no longer supported.
Please use `--track` or `--set-upstream-to` instead.
--u <upstream>::
---set-upstream-to=<upstream>::
- Set up <branchname>'s tracking information so <upstream> is
- considered <branchname>'s upstream branch. If no <branchname>
+`-u <upstream>`::
+`--set-upstream-to=<upstream>`::
+ Set up _<branch-name>_'s tracking information so _<upstream>_ is
+ considered _<branch-name>_'s upstream branch. If no _<branch-name>_
is specified, then it defaults to the current branch.
---unset-upstream::
- Remove the upstream information for <branchname>. If no branch
+`--unset-upstream`::
+ Remove the upstream information for _<branch-name>_. If no branch
is specified it defaults to the current branch.
---edit-description::
+`--edit-description`::
Open an editor and edit the text to explain what the branch is
for, to be used by various other commands (e.g. `format-patch`,
`request-pull`, and `merge` (if enabled)). Multi-line explanations
may be used.
---contains [<commit>]::
- Only list branches which contain the specified commit (HEAD
+`--contains [<commit>]`::
+ Only list branches which contain _<commit>_ (`HEAD`
if not specified). Implies `--list`.
---no-contains [<commit>]::
- Only list branches which don't contain the specified commit
- (HEAD if not specified). Implies `--list`.
+`--no-contains [<commit>]`::
+ Only list branches which don't contain _<commit>_
+ (`HEAD` if not specified). Implies `--list`.
---merged [<commit>]::
- Only list branches whose tips are reachable from the
- specified commit (HEAD if not specified). Implies `--list`.
+`--merged [<commit>]`::
+ Only list branches whose tips are reachable from
+ _<commit>_ (`HEAD` if not specified). Implies `--list`.
---no-merged [<commit>]::
- Only list branches whose tips are not reachable from the
- specified commit (HEAD if not specified). Implies `--list`.
+`--no-merged [<commit>]`::
+ Only list branches whose tips are not reachable from
+ _<commit>_ (`HEAD` if not specified). Implies `--list`.
-<branchname>::
+`--points-at <object>`::
+ Only list branches of _<object>_.
+
+`--format <format>`::
+ A string that interpolates `%(fieldname)` from a branch ref being shown
+ and the object it points at. _<format>_ is the same as
+ that of linkgit:git-for-each-ref[1].
+
+_<branch-name>_::
The name of the branch to create or delete.
The new branch name must pass all checks defined by
linkgit:git-check-ref-format[1]. Some of these checks
may restrict the characters allowed in a branch name.
-<start-point>::
+_<start-point>_::
The new branch head will point to this commit. It may be
given as a branch name, a commit-id, or a tag. If this
- option is omitted, the current HEAD will be used instead.
+ option is omitted, the current `HEAD` will be used instead.
-<oldbranch>::
+_<old-branch>_::
The name of an existing branch. If this option is omitted,
the name of the current branch will be used instead.
-<newbranch>::
+_<new-branch>_::
The new name for an existing branch. The same restrictions as for
- <branchname> apply.
-
---sort=<key>::
- Sort based on the key given. Prefix `-` to sort in descending
- order of the value. You may use the --sort=<key> option
- multiple times, in which case the last key becomes the primary
- key. The keys supported are the same as those in `git
- for-each-ref`. Sort order defaults to the value configured for the
- `branch.sort` variable if it exists, or to sorting based on the
- full refname (including `refs/...` prefix). This lists
- detached HEAD (if present) first, then local branches and
- finally remote-tracking branches. See linkgit:git-config[1].
-
-
---points-at <object>::
- Only list branches of the given object.
-
---format <format>::
- A string that interpolates `%(fieldname)` from a branch ref being shown
- and the object it points at. The format is the same as
- that of linkgit:git-for-each-ref[1].
+ _<branch-name>_ apply.
CONFIGURATION
-------------
@@ -374,7 +373,7 @@ $ git branch -D test <2>
------------
+
<1> Delete the remote-tracking branches "todo", "html" and "man". The next
- 'fetch' or 'pull' will create them again unless you configure them not to.
+ `git fetch` or `git pullè will create them again unless you configure them not to.
See linkgit:git-fetch[1].
<2> Delete the "test" branch even if the "master" branch (or whichever branch
is currently checked out) does not have all commits from the test branch.
@@ -386,8 +385,8 @@ $ git branch -r -l '<remote>/<pattern>' <1>
$ git for-each-ref 'refs/remotes/<remote>/<pattern>' <2>
------------
+
-<1> Using `-a` would conflate <remote> with any local branches you happen to
- have been prefixed with the same <remote> pattern.
+<1> Using `-a` would conflate _<remote>_ with any local branches you happen to
+ have been prefixed with the same _<remote>_ pattern.
<2> `for-each-ref` can take a wide range of options. See linkgit:git-for-each-ref[1]
Patterns will normally need quoting.
@@ -396,24 +395,24 @@ NOTES
-----
If you are creating a branch that you want to switch to immediately,
-it is easier to use the "git switch" command with its `-c` option to
+it is easier to use the `git switch` command with its `-c` option to
do the same thing with a single command.
The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
serve four related but different purposes:
- `--contains <commit>` is used to find all branches which will need
- special attention if <commit> were to be rebased or amended, since those
- branches contain the specified <commit>.
+ special attention if _<commit>_ were to be rebased or amended, since those
+ branches contain the specified _<commit>_.
- `--no-contains <commit>` is the inverse of that, i.e. branches that don't
- contain the specified <commit>.
+ contain the specified _<commit>_.
- `--merged` is used to find all branches which can be safely deleted,
- since those branches are fully contained by HEAD.
+ since those branches are fully contained by `HEAD`.
- `--no-merged` is used to find branches which are candidates for merging
- into HEAD, since those branches are not fully contained by HEAD.
+ into `HEAD`, since those branches are not fully contained by `HEAD`.
include::ref-reachability-filters.adoc[]
@@ -422,8 +421,8 @@ SEE ALSO
linkgit:git-check-ref-format[1],
linkgit:git-fetch[1],
linkgit:git-remote[1],
-link:user-manual.html#what-is-a-branch[``Understanding history: What is
-a branch?''] in the Git User's Manual.
+link:user-manual.html#what-is-a-branch["Understanding history: What is
+a branch?"] in the Git User's Manual.
GIT
---
diff --git a/Documentation/git-cat-file.adoc b/Documentation/git-cat-file.adoc
index d5890ae368..30359f5dbd 100644
--- a/Documentation/git-cat-file.adoc
+++ b/Documentation/git-cat-file.adoc
@@ -322,10 +322,10 @@ of `%(objectsize)` bytes), followed by a newline.
For example, `--batch` without a custom format would produce:
-------------
+-----------
<oid> SP <type> SP <size> LF
<contents> LF
-------------
+-----------
Whereas `--batch-check='%(objectname) %(objecttype)'` would produce:
diff --git a/Documentation/git-check-attr.adoc b/Documentation/git-check-attr.adoc
index cb5a6c8f33..503b644657 100644
--- a/Documentation/git-check-attr.adoc
+++ b/Documentation/git-check-attr.adoc
@@ -76,6 +76,7 @@ EXAMPLES
--------
In the examples, the following '.gitattributes' file is used:
+
---------------
*.java diff=java -crlf myAttr
NoMyAttr.java !myAttr
@@ -83,12 +84,14 @@ README caveat=unspecified
---------------
* Listing a single attribute:
++
---------------
$ git check-attr diff org/example/MyClass.java
org/example/MyClass.java: diff: java
---------------
* Listing multiple attributes for a file:
++
---------------
$ git check-attr crlf diff myAttr -- org/example/MyClass.java
org/example/MyClass.java: crlf: unset
@@ -97,6 +100,7 @@ org/example/MyClass.java: myAttr: set
---------------
* Listing all attributes for a file:
++
---------------
$ git check-attr --all -- org/example/MyClass.java
org/example/MyClass.java: diff: java
@@ -104,6 +108,7 @@ org/example/MyClass.java: myAttr: set
---------------
* Listing an attribute for multiple files:
++
---------------
$ git check-attr myAttr -- org/example/MyClass.java org/example/NoMyAttr.java
org/example/MyClass.java: myAttr: set
@@ -111,6 +116,7 @@ org/example/NoMyAttr.java: myAttr: unspecified
---------------
* Not all values are equally unambiguous:
++
---------------
$ git check-attr caveat README
README: caveat: unspecified
diff --git a/Documentation/git-clone.adoc b/Documentation/git-clone.adoc
index 510b91b5c0..222d558290 100644
--- a/Documentation/git-clone.adoc
+++ b/Documentation/git-clone.adoc
@@ -288,9 +288,9 @@ corresponding `--mirror` and `--no-tags` options instead.
`remote.<remote>.tagOpt=--no-tags` configuration. This ensures that
future `git pull` and `git fetch` won't follow any tags. Subsequent
explicit tag fetches will still work (see linkgit:git-fetch[1]).
-
- By default, tags are cloned and passing `--tags` is thus typically a
- no-op, unless it cancels out a previous `--no-tags`.
++
+By default, tags are cloned and passing `--tags` is thus typically a
+no-op, unless it cancels out a previous `--no-tags`.
+
Can be used in conjunction with `--single-branch` to clone and
maintain a branch with no references other than a single cloned
diff --git a/Documentation/git-column.adoc b/Documentation/git-column.adoc
index 85fb87c94a..5a4f2b6fde 100644
--- a/Documentation/git-column.adoc
+++ b/Documentation/git-column.adoc
@@ -50,6 +50,7 @@ EXAMPLES
--------
Format data by columns:
++
------------
$ seq 1 24 | git column --mode=column --padding=5
1 4 7 10 13 16 19 22
@@ -58,6 +59,7 @@ $ seq 1 24 | git column --mode=column --padding=5
------------
Format data by rows:
++
------------
$ seq 1 21 | git column --mode=row --padding=5
1 2 3 4 5 6 7
@@ -66,6 +68,7 @@ $ seq 1 21 | git column --mode=row --padding=5
------------
List some tags in a table with unequal column widths:
++
------------
$ git tag --list 'v2.4.*' --column=row,dense
v2.4.0 v2.4.0-rc0 v2.4.0-rc1 v2.4.0-rc2 v2.4.0-rc3
diff --git a/Documentation/git-cvsserver.adoc b/Documentation/git-cvsserver.adoc
index 4c475efeab..fe822f571d 100644
--- a/Documentation/git-cvsserver.adoc
+++ b/Documentation/git-cvsserver.adoc
@@ -125,9 +125,11 @@ creation in your platform (e.g. mkpasswd in Linux, encrypt in OpenBSD or
pwhash in NetBSD) and paste it in the right location.
Then provide your password via the pserver method, for example:
+
------
cvs -d:pserver:someuser:somepassword@server:/path/repo.git co <HEAD_name>
------
+
No special setup is needed for SSH access, other than having Git tools
in the PATH. If you have clients that do not accept the CVS_SERVER
environment variable, you can rename 'git-cvsserver' to `cvs`.
@@ -138,6 +140,7 @@ CVS_SERVER directly in CVSROOT like
------
cvs -d ":ext;CVS_SERVER=git cvsserver:user@server/path/repo.git" co <HEAD_name>
------
+
This has the advantage that it will be saved in your 'CVS/Root' files and
you don't need to worry about always setting the correct environment
variable. SSH users restricted to 'git-shell' don't need to override the default
@@ -168,6 +171,7 @@ All configuration variables can also be overridden for a specific method of
access. Valid method names are "ext" (for SSH access) and "pserver". The
following example configuration would disable pserver access while still
allowing access over SSH.
+
------
[gitcvs]
enabled=0
diff --git a/Documentation/git-diff-pairs.adoc b/Documentation/git-diff-pairs.adoc
new file mode 100644
index 0000000000..f99fcd1ead
--- /dev/null
+++ b/Documentation/git-diff-pairs.adoc
@@ -0,0 +1,60 @@
+git-diff-pairs(1)
+=================
+
+NAME
+----
+git-diff-pairs - Compare the content and mode of provided blob pairs
+
+SYNOPSIS
+--------
+[synopsis]
+git diff-pairs -z [<diff-options>]
+
+DESCRIPTION
+-----------
+Show changes for file pairs provided on stdin. Input for this command must be
+in the NUL-terminated raw output format as generated by commands such as `git
+diff-tree -z -r --raw`. By default, the outputted diffs are computed and shown
+in the patch format when stdin closes.
+
+A single NUL byte may be written to stdin between raw input lines to compute
+file pair diffs up to that point instead of waiting for stdin to close. A NUL
+byte is also written to the output to delimit between these batches of diffs.
+
+Usage of this command enables the traditional diff pipeline to be broken up
+into separate stages where `diff-pairs` acts as the output phase. Other
+commands, such as `diff-tree`, may serve as a frontend to compute the raw
+diff format used as input.
+
+Instead of computing diffs via `git diff-tree -p -M` in one step, `diff-tree`
+can compute the file pairs and rename information without the blob diffs. This
+output can be fed to `diff-pairs` to generate the underlying blob diffs as done
+in the following example:
+
+-----------------------------
+git diff-tree -z -r -M $a $b |
+git diff-pairs -z
+-----------------------------
+
+Computing the tree diff upfront with rename information allows patch output
+from `diff-pairs` to be progressively computed over the course of potentially
+multiple invocations.
+
+Pathspecs are not currently supported by `diff-pairs`. Pathspec limiting should
+be performed by the upstream command generating the raw diffs used as input.
+
+Tree objects are not currently supported as input and are rejected.
+
+Abbreviated object IDs in the `diff-pairs` input are not supported. Outputted
+object IDs can be abbreviated using the `--abbrev` option.
+
+OPTIONS
+-------
+
+include::diff-options.adoc[]
+
+include::diff-generate-patch.adoc[]
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-fast-export.adoc b/Documentation/git-fast-export.adoc
index 752e4b9b01..413a527496 100644
--- a/Documentation/git-fast-export.adoc
+++ b/Documentation/git-fast-export.adoc
@@ -27,17 +27,33 @@ OPTIONS
Insert 'progress' statements every <n> objects, to be shown by
'git fast-import' during import.
---signed-tags=(verbatim|warn|warn-strip|strip|abort)::
+--signed-tags=(verbatim|warn-verbatim|warn-strip|strip|abort)::
Specify how to handle signed tags. Since any transformation
- after the export can change the tag names (which can also happen
- when excluding revisions) the signatures will not match.
+ after the export (or during the export, such as excluding
+ revisions) can change the hashes being signed, the signatures
+ may become invalid.
+
When asking to 'abort' (which is the default), this program will die
when encountering a signed tag. With 'strip', the tags will silently
be made unsigned, with 'warn-strip' they will be made unsigned but a
warning will be displayed, with 'verbatim', they will be silently
-exported and with 'warn', they will be exported, but you will see a
-warning.
+exported and with 'warn-verbatim' (or 'warn', a deprecated synonym),
+they will be exported, but you will see a warning. 'verbatim' and
+'warn-verbatim' should only be used if you know that no transformation
+affecting tags or any commit in their history will be performed by you
+or by fast-export or fast-import, or if you do not care that the
+resulting tag will have an invalid signature.
+
+--signed-commits=(verbatim|warn-verbatim|warn-strip|strip|abort)::
+ Specify how to handle signed commits. Behaves exactly as
+ '--signed-tags', but for commits. Default is 'abort'.
++
+Earlier versions this command that did not have '--signed-commits'
+behaved as if '--signed-commits=strip'. As an escape hatch for users
+of tools that call 'git fast-export' but do not yet support
+'--signed-commits', you may set the environment variable
+'FAST_EXPORT_SIGNED_COMMITS_NOABORT=1' in order to change the default
+from 'abort' to 'warn-strip'.
--tag-of-filtered-object=(abort|drop|rewrite)::
Specify how to handle tags whose tagged object is filtered out.
diff --git a/Documentation/git-fast-import.adoc b/Documentation/git-fast-import.adoc
index 58a2eaa51a..7b107f5e8e 100644
--- a/Documentation/git-fast-import.adoc
+++ b/Documentation/git-fast-import.adoc
@@ -431,13 +431,22 @@ and control the current import process. More detailed discussion
Create or update a branch with a new commit, recording one logical
change to the project.
+////
+Yes, it's intentional that the 'gpgsig' line doesn't have a trailing
+`LF`; the definition of `data` has a byte-count prefix, so it
+doesn't need an `LF` to act as a terminator (and `data` also already
+includes an optional trailing `LF?` just in case you want to include
+one).
+////
+
....
'commit' SP <ref> LF
mark?
original-oid?
('author' (SP <name>)? SP LT <email> GT SP <when> LF)?
'committer' (SP <name>)? SP LT <email> GT SP <when> LF
- ('encoding' SP <encoding>)?
+ ('gpgsig' SP <alg> LF data)?
+ ('encoding' SP <encoding> LF)?
data
('from' SP <commit-ish> LF)?
('merge' SP <commit-ish> LF)*
@@ -505,6 +514,15 @@ that was selected by the --date-format=<fmt> command-line option.
See ``Date Formats'' above for the set of supported formats, and
their syntax.
+`gpgsig`
+^^^^^^^^
+
+The optional `gpgsig` command is used to include a PGP/GPG signature
+that signs the commit data.
+
+Here <alg> specifies which hashing algorithm is used for this
+signature, either `sha1` or `sha256`.
+
`encoding`
^^^^^^^^^^
The optional `encoding` command indicates the encoding of the commit
diff --git a/Documentation/git-for-each-ref.adoc b/Documentation/git-for-each-ref.adoc
index ffb97e62c2..5ef89fc0fe 100644
--- a/Documentation/git-for-each-ref.adoc
+++ b/Documentation/git-for-each-ref.adoc
@@ -441,6 +441,7 @@ Ref: %(*refname)
A simple example showing the use of shell eval on the output,
demonstrating the use of --shell. List the prefixes of all heads:
+
------------
#!/bin/sh
@@ -455,6 +456,7 @@ done
A bit more elaborate report on tags, demonstrating that the format
may be an entire script:
+
------------
#!/bin/sh
diff --git a/Documentation/git-fsck.adoc b/Documentation/git-fsck.adoc
index 8f32800a83..11203ba925 100644
--- a/Documentation/git-fsck.adoc
+++ b/Documentation/git-fsck.adoc
@@ -12,7 +12,7 @@ SYNOPSIS
'git fsck' [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]
[--[no-]full] [--strict] [--verbose] [--lost-found]
[--[no-]dangling] [--[no-]progress] [--connectivity-only]
- [--[no-]name-objects] [<object>...]
+ [--[no-]name-objects] [--[no-]references] [<object>...]
DESCRIPTION
-----------
@@ -104,6 +104,11 @@ care about this output and want to speed it up further.
progress status even if the standard error stream is not
directed to a terminal.
+--[no-]references::
+ Control whether to check the references database consistency
+ via 'git refs verify'. See linkgit:git-refs[1] for details.
+ The default is to check the references database.
+
CONFIGURATION
-------------
diff --git a/Documentation/git-p4.adoc b/Documentation/git-p4.adoc
index de5ee6748e..f97b786bf9 100644
--- a/Documentation/git-p4.adoc
+++ b/Documentation/git-p4.adoc
@@ -80,6 +80,7 @@ This:
To reproduce the entire p4 history in Git, use the '@all' modifier on
the depot path:
+
------------
$ git p4 clone //depot/path/project@all
------------
@@ -89,19 +90,23 @@ Sync
~~~~
As development continues in the p4 repository, those changes can
be included in the Git repository using:
+
------------
$ git p4 sync
------------
+
This command finds new changes in p4 and imports them as Git commits.
P4 repositories can be added to an existing Git repository using
'git p4 sync' too:
+
------------
$ mkdir repo-git
$ cd repo-git
$ git init
$ git p4 sync //path/in/your/perforce/depot
------------
+
This imports the specified depot into
'refs/remotes/p4/master' in an existing Git repository. The
`--branch` option can be used to specify a different branch to
@@ -125,6 +130,7 @@ and merge them with local uncommitted changes. Often, the p4 repository
is the ultimate location for all code, thus a rebase workflow makes
sense. This command does 'git p4 sync' followed by 'git rebase' to move
local commits on top of updated p4 changes.
+
------------
$ git p4 rebase
------------
@@ -140,16 +146,19 @@ will be created and populated if it does not already exist.
To submit all changes that are in the current Git branch but not in
the 'p4/master' branch, use:
+
------------
$ git p4 submit
------------
To specify a branch other than the current one, use:
+
------------
$ git p4 submit topicbranch
------------
To specify a single commit or a range of commits, use:
+
------------
$ git p4 submit --commit <sha1>
$ git p4 submit --commit <sha1..sha1>
@@ -510,20 +519,24 @@ when cloning or syncing to have 'git p4' automatically find
subdirectories in p4, and to generate these as branches in Git.
For example, if the P4 repository structure is:
+
----
//depot/main/...
//depot/branch1/...
----
And "p4 branch -o branch1" shows a View line that looks like:
+
----
//depot/main/... //depot/branch1/...
----
Then this 'git p4 clone' command:
+
----
git p4 clone --detect-branches //depot@all
----
+
produces a separate branch in 'refs/remotes/p4/' for //depot/main,
called 'master', and one for //depot/branch1 called 'depot/branch1'.
@@ -536,6 +549,7 @@ simple p4 branch specification, where the "source" and "destination" are
the path elements in the p4 repository. The example above relied on the
presence of the p4 branch. Without p4 branches, the same result will
occur with:
+
----
git init depot
cd depot
diff --git a/Documentation/git-pack-refs.adoc b/Documentation/git-pack-refs.adoc
index 2dcabaf74c..652c549771 100644
--- a/Documentation/git-pack-refs.adoc
+++ b/Documentation/git-pack-refs.adoc
@@ -88,10 +88,10 @@ Do not pack refs matching the given `glob(7)` pattern. Repetitions of this optio
accumulate exclusion patterns. Use `--no-exclude` to clear and reset the list of
patterns. If a ref is already packed, including it with `--exclude` will not
unpack it.
-
++
When used with `--all`, pack only loose refs which do not match any of
the provided `--exclude` patterns.
-
++
When used with `--include`, refs provided to `--include`, minus refs that are
provided to `--exclude` will be packed.
diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
index 153cb69a4f..956d3048f5 100644
--- a/Documentation/git-rebase.adoc
+++ b/Documentation/git-rebase.adoc
@@ -1107,10 +1107,12 @@ In that case, the fix is easy because 'git rebase' knows to skip
changes that are already present in the new upstream (unless
`--reapply-cherry-picks` is given). So if you say
(assuming you're on 'topic')
+
------------
$ git rebase subsystem
------------
you will end up with the fixed history
+
------------
o---o---o---o---o---o---o---o master
\
@@ -1145,6 +1147,7 @@ of the old 'subsystem', for example:
You can then transplant the old `subsystem..topic` to the new tip by
saying (for the reflog case, and assuming you are on 'topic' already):
+
------------
$ git rebase --onto subsystem subsystem@{1}
------------
diff --git a/Documentation/git-repack.adoc b/Documentation/git-repack.adoc
index 5852a5c973..e1cd75eebe 100644
--- a/Documentation/git-repack.adoc
+++ b/Documentation/git-repack.adoc
@@ -77,15 +77,18 @@ to the new separate pack will be written.
Only useful with `--cruft -d`.
--max-cruft-size=<n>::
- Repack cruft objects into packs as large as `<n>` bytes before
- creating new packs. As long as there are enough cruft packs
- smaller than `<n>`, repacking will cause a new cruft pack to
- be created containing objects from any combined cruft packs,
- along with any new unreachable objects. Cruft packs larger than
- `<n>` will not be modified. When the new cruft pack is larger
- than `<n>` bytes, it will be split into multiple packs, all of
- which are guaranteed to be at most `<n>` bytes in size. Only
- useful with `--cruft -d`.
+ Overrides `--max-pack-size` for cruft packs. Inherits the value of
+ `--max-pack-size` (if any) by default. See the documentation for
+ `--max-pack-size` for more details.
+
+--combine-cruft-below-size=<n>::
+ When generating cruft packs without pruning, only repack
+ existing cruft packs whose size is strictly less than `<n>`,
+ where `<n>` represents a number of bytes, which can optionally
+ be suffixed with "k", "m", or "g". Cruft packs whose size is
+ greater than or equal to `<n>` are left as-is and not repacked.
+ Useful when you want to avoid repacking large cruft pack(s) in
+ repositories that have many and/or large unreachable objects.
--expire-to=<dir>::
Write a cruft pack containing pruned objects (if any) to the
diff --git a/Documentation/git-restore.adoc b/Documentation/git-restore.adoc
index 751f01b441..877b7772e6 100644
--- a/Documentation/git-restore.adoc
+++ b/Documentation/git-restore.adoc
@@ -51,9 +51,6 @@ leave out at most one of _<rev-A>__ and _<rev-B>_, in which case it defaults to
restore source and the restore location. See the "Interactive
Mode" section of linkgit:git-add[1] to learn how to operate
the `--patch` mode.
-+
-Note that `--patch` can accept no pathspec and will prompt to restore
-all modified paths.
`-W`::
`--worktree`::
diff --git a/Documentation/gitattributes.adoc b/Documentation/gitattributes.adoc
index 7eaca89972..f20041a323 100644
--- a/Documentation/gitattributes.adoc
+++ b/Documentation/gitattributes.adoc
@@ -513,7 +513,7 @@ If the filter command (a string value) is defined via
`filter.<driver>.process` then Git can process all blobs with a
single filter invocation for the entire life of a single Git
command. This is achieved by using the long-running process protocol
-(described in technical/long-running-process-protocol.txt).
+(described in Documentation/technical/long-running-process-protocol.adoc).
When Git encounters the first file that needs to be cleaned or smudged,
it starts the filter and performs the handshake. In the handshake, the
@@ -531,13 +531,14 @@ must not send any response before it received the content and the
final flush packet. Also note that the "value" of a "key=value" pair
can contain the "=" character whereas the key would never contain
that character.
-------------------------
+
+-----------------------
packet: git> command=smudge
packet: git> pathname=path/testfile.dat
packet: git> 0000
packet: git> CONTENT
packet: git> 0000
-------------------------
+-----------------------
The filter is expected to respond with a list of "key=value" pairs
terminated with a flush packet. If the filter does not experience
@@ -559,6 +560,7 @@ packet: git< 0000 # empty list, keep "status=success" unchanged!
If the result content is empty then the filter is expected to respond
with a "success" status and a flush packet to signal the empty content.
+
------------------------
packet: git< status=success
packet: git< 0000
@@ -568,14 +570,16 @@ packet: git< 0000 # empty list, keep "status=success" unchanged!
In case the filter cannot or does not want to process the content,
it is expected to respond with an "error" status.
-------------------------
+
+-----------------------
packet: git< status=error
packet: git< 0000
-------------------------
+-----------------------
If the filter experiences an error during processing, then it can
send the status "error" after the content was (partially or
completely) sent.
+
------------------------
packet: git< status=success
packet: git< 0000
@@ -589,10 +593,11 @@ In case the filter cannot or does not want to process the content
as well as any future content for the lifetime of the Git process,
then it is expected to respond with an "abort" status at any point
in the protocol.
-------------------------
+
+-----------------------
packet: git< status=abort
packet: git< 0000
-------------------------
+-----------------------
Git neither stops nor restarts the filter process in case the
"error"/"abort" status is set. However, Git sets its exit code
@@ -613,7 +618,8 @@ flag "can-delay" after the filter command and pathname. This flag
denotes that the filter can delay filtering the current blob (e.g. to
compensate network latencies) by responding with no content but with
the status "delayed" and a flush packet.
-------------------------
+
+-----------------------
packet: git> command=smudge
packet: git> pathname=path/testfile.dat
packet: git> can-delay=1
@@ -622,7 +628,7 @@ packet: git> CONTENT
packet: git> 0000
packet: git< status=delayed
packet: git< 0000
-------------------------
+-----------------------
If the filter supports the "delay" capability then it must support the
"list_available_blobs" command. If Git sends this command, then the
@@ -647,10 +653,12 @@ packet: git< status=success
packet: git< 0000
------------------------
+
After Git received the pathnames, it will request the corresponding
blobs again. These requests contain a pathname and an empty content
section. The filter is expected to respond with the smudged content
in the usual way as explained above.
+
------------------------
packet: git> command=smudge
packet: git> pathname=path/testfile.dat
@@ -1177,11 +1185,11 @@ integer has a meaningful effect.
For example, this line in `.gitattributes` can be used to tell the merge
machinery to leave much longer (instead of the usual 7-character-long)
-conflict markers when merging the file `Documentation/git-merge.txt`
+conflict markers when merging the file `Documentation/git-merge.adoc`
results in a conflict.
------------------------
-Documentation/git-merge.txt conflict-marker-size=32
+Documentation/git-merge.adoc conflict-marker-size=32
------------------------
diff --git a/Documentation/gitcli.adoc b/Documentation/gitcli.adoc
index 04193ec907..1ea681b59d 100644
--- a/Documentation/gitcli.adoc
+++ b/Documentation/gitcli.adoc
@@ -209,13 +209,13 @@ $ git foo -o Arg
However, this is *NOT* allowed for switches with an optional value, where the
'stuck' form must be used:
+
----------------------------
$ git describe --abbrev HEAD # correct
$ git describe --abbrev=10 HEAD # correct
$ git describe --abbrev 10 HEAD # NOT WHAT YOU MEANT
----------------------------
-
NOTES ON FREQUENTLY CONFUSED OPTIONS
------------------------------------
diff --git a/Documentation/gitprotocol-common.adoc b/Documentation/gitprotocol-common.adoc
index cdc9d6e707..b4a5316ca4 100644
--- a/Documentation/gitprotocol-common.adoc
+++ b/Documentation/gitprotocol-common.adoc
@@ -21,11 +21,13 @@ ABNF Notation
ABNF notation as described by RFC 5234 is used within the protocol documents,
except the following replacement core rules are used:
+
----
HEXDIG = DIGIT / "a" / "b" / "c" / "d" / "e" / "f"
----
We also define the following common rules:
+
----
NUL = %x00
zero-id = 40*"0"
diff --git a/Documentation/gitprotocol-v2.adoc b/Documentation/gitprotocol-v2.adoc
index 9f6350bbf2..5598c93e67 100644
--- a/Documentation/gitprotocol-v2.adoc
+++ b/Documentation/gitprotocol-v2.adoc
@@ -785,6 +785,60 @@ retrieving the header from a bundle at the indicated URI, and thus
save themselves and the server(s) the request(s) needed to inspect the
headers of that bundle or bundles.
+promisor-remote=<pr-infos>
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The server may advertise some promisor remotes it is using or knows
+about to a client which may want to use them as its promisor remotes,
+instead of this repository. In this case <pr-infos> should be of the
+form:
+
+ pr-infos = pr-info | pr-infos ";" pr-info
+
+ pr-info = "name=" pr-name | "name=" pr-name "," "url=" pr-url
+
+where `pr-name` is the urlencoded name of a promisor remote, and
+`pr-url` the urlencoded URL of that promisor remote.
+
+In this case, if the client decides to use one or more promisor
+remotes the server advertised, it can reply with
+"promisor-remote=<pr-names>" where <pr-names> should be of the form:
+
+ pr-names = pr-name | pr-names ";" pr-name
+
+where `pr-name` is the urlencoded name of a promisor remote the server
+advertised and the client accepts.
+
+Note that, everywhere in this document, `pr-name` MUST be a valid
+remote name, and the ';' and ',' characters MUST be encoded if they
+appear in `pr-name` or `pr-url`.
+
+If the server doesn't know any promisor remote that could be good for
+a client to use, or prefers a client not to use any promisor remote it
+uses or knows about, it shouldn't advertise the "promisor-remote"
+capability at all.
+
+In this case, or if the client doesn't want to use any promisor remote
+the server advertised, the client shouldn't advertise the
+"promisor-remote" capability at all in its reply.
+
+The "promisor.advertise" and "promisor.acceptFromServer" configuration
+options can be used on the server and client side to control what they
+advertise or accept respectively. See the documentation of these
+configuration options for more information.
+
+Note that in the future it would be nice if the "promisor-remote"
+protocol capability could be used by the server, when responding to
+`git fetch` or `git clone`, to advertise better-connected remotes that
+the client can use as promisor remotes, instead of this repository, so
+that the client can lazily fetch objects from these other
+better-connected remotes. This would require the server to omit in its
+response the objects available on the better-connected remotes that
+the client has accepted. This hasn't been implemented yet though. So
+for now this "promisor-remote" capability is useful only when the
+server advertises some promisor remotes it already uses to borrow
+objects from.
+
GIT
---
Part of the linkgit:git[1] suite
diff --git a/Documentation/gitrepository-layout.adoc b/Documentation/gitrepository-layout.adoc
index 6348ef1dcd..7421ef956d 100644
--- a/Documentation/gitrepository-layout.adoc
+++ b/Documentation/gitrepository-layout.adoc
@@ -152,6 +152,7 @@ config.worktree::
working directory in multiple working directory setup (see
linkgit:git-worktree[1]).
+ifndef::with-breaking-changes[]
branches::
A deprecated way to store shorthands to be used
to specify a URL to 'git fetch', 'git pull' and 'git push'.
@@ -164,6 +165,7 @@ branches::
"$GIT_COMMON_DIR/branches" will be used instead.
+
Git will stop reading remotes from this directory in Git 3.0.
+endif::with-breaking-changes[]
hooks::
Hooks are customization scripts used by various Git
@@ -231,6 +233,7 @@ info/sparse-checkout::
This file stores sparse checkout patterns.
See also: linkgit:git-read-tree[1].
+ifndef::with-breaking-changes[]
remotes::
Stores shorthands for URL and default refnames for use
when interacting with remote repositories via 'git fetch',
@@ -241,6 +244,7 @@ remotes::
"$GIT_COMMON_DIR/remotes" will be used instead.
+
Git will stop reading remotes from this directory in Git 3.0.
+endif::with-breaking-changes[]
logs::
Records of changes made to refs are stored in this directory.
diff --git a/Documentation/gitweb.adoc b/Documentation/gitweb.adoc
index 5e2b491ec2..4261f9e235 100644
--- a/Documentation/gitweb.adoc
+++ b/Documentation/gitweb.adoc
@@ -103,6 +103,7 @@ You can generate the projects list index file using the project_index action
"Generating projects list using gitweb" section below.
Example contents:
+
-----------------------------------------------------------------------
foo.git Joe+R+Hacker+<joe@example.com>
foo/bar.git O+W+Ner+<owner@example.org>
@@ -124,6 +125,7 @@ Generating projects list using gitweb
We assume that GITWEB_CONFIG has its default Makefile value, namely
'gitweb_config.perl'. Put the following in 'gitweb_make_index.perl' file:
+
----------------------------------------------------------------------------
read_config_file("gitweb_config.perl");
$projects_list = $projectroot;
@@ -518,12 +520,14 @@ rules.
If you use the rewrite rules from the example you *might* also need
something like the following in your gitweb configuration file
(`/etc/gitweb.conf` following example):
+
----------------------------------------------------------------------------
@stylesheets = ("/some/absolute/path/gitweb.css");
$my_uri = "/";
$home_link = "/";
$per_request_config = 1;
----------------------------------------------------------------------------
+
Nowadays though gitweb should create HTML base tag when needed (to set base
URI for relative links), so it should work automatically.
@@ -535,6 +539,7 @@ Apache virtual host and gitweb configuration files in the following way.
The virtual host configuration (in Apache configuration file) should look
like this:
+
--------------------------------------------------------------------------
<VirtualHost *:80>
ServerName git.example.org
@@ -575,9 +580,11 @@ like this:
Here actual project root is passed to gitweb via `GITWEB_PROJECT_ROOT`
environment variable from a web server, so you need to put the following
line in gitweb configuration file (`/etc/gitweb.conf` in above example):
+
--------------------------------------------------------------------------
$projectroot = $ENV{'GITWEB_PROJECTROOT'} || "/pub/git";
--------------------------------------------------------------------------
+
*Note* that this requires to be set for each request, so either
`$per_request_config` must be false, or the above must be put in code
referenced by `$per_request_config`;
@@ -604,9 +611,11 @@ the third and the fourth.
PATH_INFO usage
~~~~~~~~~~~~~~~
If you enable PATH_INFO usage in gitweb by putting
+
----------------------------------------------------------------------------
$feature{'pathinfo'}{'default'} = [1];
----------------------------------------------------------------------------
+
in your gitweb configuration file, it is possible to set up your server so
that it consumes and produces URLs in the form
@@ -636,6 +645,7 @@ complementary static files (stylesheet, favicon, JavaScript):
</Directory>
</VirtualHost>
----------------------------------------------------------------------------
+
The rewrite rule guarantees that existing static files will be properly
served, whereas any other URL will be passed to gitweb as PATH_INFO
parameter.
@@ -647,6 +657,7 @@ for fetching" section). A possible workaround for the latter is the
following: in your project root dir (e.g. `/pub/git`) have the projects
named *without* a .git extension (e.g. `/pub/git/project` instead of
`/pub/git/project.git`) and configure Apache as follows:
+
----------------------------------------------------------------------------
<VirtualHost *:80>
ServerAlias git.example.com
diff --git a/Documentation/gitweb.conf.adoc b/Documentation/gitweb.conf.adoc
index 85983587fc..1348e9b125 100644
--- a/Documentation/gitweb.conf.adoc
+++ b/Documentation/gitweb.conf.adoc
@@ -603,6 +603,7 @@ 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)>,
@@ -613,6 +614,7 @@ structure:
Some features cannot be overridden per project. For those
features the structure of appropriate `%feature` hash element has a simpler
form:
+
----------------------------------------------------------------------
"<feature-name>" => {
"override" => 0,
diff --git a/Documentation/howto/howto-index.sh b/Documentation/howto/howto-index.sh
index eecd123a93..ace49830a8 100755
--- a/Documentation/howto/howto-index.sh
+++ b/Documentation/howto/howto-index.sh
@@ -9,9 +9,9 @@ people describing how they use Git in their workflow.
EOF
-for txt
+for adoc
do
- title=$(expr "$txt" : '.*/\(.*\)\.txt$')
+ title=$(expr "$adoc" : '.*/\(.*\)\.adoc$')
from=$(sed -ne '
/^$/q
/^From:[ ]/{
@@ -21,7 +21,7 @@ do
s/^/by /
p
}
- ' "$txt")
+ ' "$adoc")
abstract=$(sed -ne '
/^Abstract:[ ]/{
@@ -39,13 +39,13 @@ do
x
p
q
- }' "$txt")
+ }' "$adoc")
- if grep 'Content-type: text/asciidoc' >/dev/null $txt
+ if grep 'Content-type: text/asciidoc' >/dev/null $adoc
then
- file=$(expr "$txt" : '\(.*\)\.txt$').html
+ file=$(expr "$adoc" : '\(.*\)\.adoc$').html
else
- file="$txt"
+ file="$adoc"
fi
echo "* link:howto/$(basename "$file")[$title] $from
diff --git a/Documentation/howto/meson.build b/Documentation/howto/meson.build
index 92a08b13ee..81000028c0 100644
--- a/Documentation/howto/meson.build
+++ b/Documentation/howto/meson.build
@@ -1,20 +1,20 @@
howto_sources = [
- 'coordinate-embargoed-releases.txt',
- 'keep-canonical-history-correct.txt',
- 'maintain-git.txt',
- 'new-command.txt',
- 'rebase-from-internal-branch.txt',
- 'rebuild-from-update-hook.txt',
- 'recover-corrupted-blob-object.txt',
- 'recover-corrupted-object-harder.txt',
- 'revert-a-faulty-merge.txt',
- 'revert-branch-rebase.txt',
- 'separating-topic-branches.txt',
- 'setup-git-server-over-http.txt',
- 'update-hook-example.txt',
- 'use-git-daemon.txt',
- 'using-merge-subtree.txt',
- 'using-signed-tag-in-pull-request.txt',
+ 'coordinate-embargoed-releases.adoc',
+ 'keep-canonical-history-correct.adoc',
+ 'maintain-git.adoc',
+ 'new-command.adoc',
+ 'rebase-from-internal-branch.adoc',
+ 'rebuild-from-update-hook.adoc',
+ 'recover-corrupted-blob-object.adoc',
+ 'recover-corrupted-object-harder.adoc',
+ 'revert-a-faulty-merge.adoc',
+ 'revert-branch-rebase.adoc',
+ 'separating-topic-branches.adoc',
+ 'setup-git-server-over-http.adoc',
+ 'update-hook-example.adoc',
+ 'use-git-daemon.adoc',
+ 'using-merge-subtree.adoc',
+ 'using-signed-tag-in-pull-request.adoc',
]
howto_index = custom_target(
@@ -26,7 +26,7 @@ howto_index = custom_target(
env: script_environment,
capture: true,
input: howto_sources,
- output: 'howto-index.txt',
+ output: 'howto-index.adoc',
)
custom_target(
diff --git a/Documentation/howto/new-command.adoc b/Documentation/howto/new-command.adoc
index 880c51112b..ac73c98be7 100644
--- a/Documentation/howto/new-command.adoc
+++ b/Documentation/howto/new-command.adoc
@@ -48,7 +48,7 @@ binary); this organization makes it easy for people reading the code
to find things.
See the CodingGuidelines document for other guidance on what we consider
-good practice in C and shell, and api-builtin.txt for the support
+good practice in C and shell, and builtin.h for the support
functions available to built-in commands written in C.
What every extension command needs
diff --git a/Documentation/merge-strategies.adoc b/Documentation/merge-strategies.adoc
index 93822ebc4e..59f5ae36cc 100644
--- a/Documentation/merge-strategies.adoc
+++ b/Documentation/merge-strategies.adoc
@@ -82,6 +82,11 @@ find-renames[=<n>];;
rename-threshold=<n>;;
Deprecated synonym for `find-renames=<n>`.
+no-renames;;
+ Turn off rename detection. This overrides the `merge.renames`
+ configuration variable.
+ See also linkgit:git-diff[1] `--no-renames`.
+
subtree[=<path>];;
This option is a more advanced form of 'subtree' strategy, where
the strategy makes a guess on how two trees must be shifted to
@@ -107,7 +112,7 @@ For a path that is a submodule, the same caution as 'ort' applies to this
strategy.
+
The 'recursive' strategy takes the same options as 'ort'. However,
-there are three additional options that 'ort' ignores (not documented
+there are two additional options that 'ort' ignores (not documented
above) that are potentially useful with the 'recursive' strategy:
patience;;
@@ -121,11 +126,6 @@ diff-algorithm=[patience|minimal|histogram|myers];;
specifically uses `diff-algorithm=histogram`, while `recursive`
defaults to the `diff.algorithm` config setting.
-no-renames;;
- Turn off rename detection. This overrides the `merge.renames`
- configuration variable.
- See also linkgit:git-diff[1] `--no-renames`.
-
resolve::
This can only resolve two heads (i.e. the current branch
and another branch you pulled from) using a 3-way merge
diff --git a/Documentation/mergetools/vimdiff.adoc b/Documentation/mergetools/vimdiff.adoc
index befa86d692..ab915df408 100644
--- a/Documentation/mergetools/vimdiff.adoc
+++ b/Documentation/mergetools/vimdiff.adoc
@@ -86,7 +86,7 @@ command.
+
--
When `MERGED` is not present in the layout, you must "mark" one of the
-buffers with an asterisk. That will become the buffer you need to edit and
+buffers with an arobase (`@`). That will become the buffer you need to edit and
save after resolving the conflicts.
....
------------------------------------------
diff --git a/Documentation/meson.build b/Documentation/meson.build
index 0a0f2bfa14..8b9e692c59 100644
--- a/Documentation/meson.build
+++ b/Documentation/meson.build
@@ -42,6 +42,7 @@ manpages = {
'git-diagnose.adoc' : 1,
'git-diff-files.adoc' : 1,
'git-diff-index.adoc' : 1,
+ 'git-diff-pairs.adoc' : 1,
'git-difftool.adoc' : 1,
'git-diff-tree.adoc' : 1,
'git-diff.adoc' : 1,
@@ -96,7 +97,6 @@ manpages = {
'git-notes.adoc' : 1,
'git-p4.adoc' : 1,
'git-pack-objects.adoc' : 1,
- 'git-pack-redundant.adoc' : 1,
'git-pack-refs.adoc' : 1,
'git-patch-id.adoc' : 1,
'git-prune-packed.adoc' : 1,
@@ -205,6 +205,14 @@ manpages = {
'gitworkflows.adoc' : 7,
}
+manpages_breaking_changes = {
+ 'git-pack-redundant.adoc' : 1,
+}
+
+if not get_option('breaking_changes')
+ manpages += manpages_breaking_changes
+endif
+
docs_backend = get_option('docs_backend')
if docs_backend == 'auto'
if find_program('asciidoc', dirs: program_path, required: false).found()
@@ -284,6 +292,10 @@ elif docs_backend == 'asciidoctor'
]
endif
+if get_option('breaking_changes')
+ asciidoc_common_options += ['--attribute', 'with-breaking-changes']
+endif
+
xmlto = find_program('xmlto', dirs: program_path)
cmd_lists = [
@@ -436,6 +448,7 @@ if get_option('docs').contains('html')
)
articles = [
+ 'BreakingChanges.adoc',
'DecisionMaking.adoc',
'MyFirstContribution.adoc',
'MyFirstObjectWalk.adoc',
@@ -475,7 +488,9 @@ endif
# Sanity check that we are not missing any tests present in 't/'. This check
# only runs once at configure time and is thus best-effort, only. Furthermore,
# it only verifies man pages for the sake of simplicity.
-configured_manpages = manpages.keys() + [ 'git-bisect-lk2009.adoc', 'git-tools.adoc' ]
+configured_manpages = manpages.keys()
+configured_manpages += manpages_breaking_changes.keys()
+configured_manpages += [ 'git-bisect-lk2009.adoc', 'git-tools.adoc' ]
actual_manpages = run_command(shell, '-c', 'ls git*.adoc scalar.adoc',
check: true,
env: script_environment,
diff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc
index 785c0786e0..1c403c1e40 100644
--- a/Documentation/rev-list-options.adoc
+++ b/Documentation/rev-list-options.adoc
@@ -429,6 +429,7 @@ filtered for `foo`, they look different and equal, respectively.)
In the following, we will always refer to the same example history to
illustrate the differences between simplification settings. We assume
that you are filtering for a file `foo` in this commit graph:
+
-----------------------------------------------------------------------
.-A---M---N---O---P---Q
/ / / / / /
@@ -436,6 +437,7 @@ that you are filtering for a file `foo` in this commit graph:
\ / / / / /
`-------------' X
-----------------------------------------------------------------------
+
The horizontal line of history A---Q is taken to be the first parent of
each merge. The commits are:
@@ -640,7 +642,7 @@ commits affected by that topic, we may only want to view the subset of
-----------------------------------------------------------------------
E
\
- G---H---I---J
+ C---G---H---I---J
\
L--M
-----------------------------------------------------------------------
diff --git a/Documentation/technical/api-simple-ipc.adoc b/Documentation/technical/api-simple-ipc.adoc
index c4fb152b23..972178b042 100644
--- a/Documentation/technical/api-simple-ipc.adoc
+++ b/Documentation/technical/api-simple-ipc.adoc
@@ -36,7 +36,7 @@ Comparison with sub-process model
---------------------------------
The Simple-IPC mechanism differs from the existing `sub-process.c`
-model (Documentation/technical/long-running-process-protocol.txt) and
+model (Documentation/technical/long-running-process-protocol.adoc) and
used by applications like Git-LFS. In the LFS-style sub-process model,
the helper is started by the foreground process, communication happens
via a pair of file descriptors bound to the stdin/stdout of the
diff --git a/Documentation/technical/hash-function-transition.adoc b/Documentation/technical/hash-function-transition.adoc
index 7102c7c8f5..f047fd80ca 100644
--- a/Documentation/technical/hash-function-transition.adoc
+++ b/Documentation/technical/hash-function-transition.adoc
@@ -394,7 +394,7 @@ inflated again in step 3, for a total of two inflations.
Step 4 is probably necessary for good read-time performance. "git
pack-objects" on the server optimizes the pack file for good data
-locality (see Documentation/technical/pack-heuristics.txt).
+locality (see Documentation/technical/pack-heuristics.adoc).
Details of this process are likely to change. It will take some
experimenting to get this to perform well.
diff --git a/Documentation/technical/large-object-promisors.adoc b/Documentation/technical/large-object-promisors.adoc
new file mode 100644
index 0000000000..dea8dafa66
--- /dev/null
+++ b/Documentation/technical/large-object-promisors.adoc
@@ -0,0 +1,656 @@
+Large Object Promisors
+======================
+
+Since Git has been created, users have been complaining about issues
+with storing large files in Git. Some solutions have been created to
+help, but they haven't helped much with some issues.
+
+Git currently supports multiple promisor remotes, which could help
+with some of these remaining issues, but it's very hard to use them to
+help, because a number of important features are missing.
+
+The goal of the effort described in this document is to add these
+important features.
+
+We will call a "Large Object Promisor", or "LOP" in short, a promisor
+remote which is used to store only large blobs and which is separate
+from the main remote that should store the other Git objects and the
+rest of the repos.
+
+By extension, we will also call "Large Object Promisor", or LOP, the
+effort described in this document to add a set of features to make it
+easier to handle large blobs/files in Git by using LOPs.
+
+This effort aims to especially improve things on the server side, and
+especially for large blobs that are already compressed in a binary
+format.
+
+This effort aims to provide an alternative to Git LFS
+(https://git-lfs.com/) and similar tools like git-annex
+(https://git-annex.branchable.com/) for handling large files, even
+though a complete alternative would very likely require other efforts
+especially on the client side, where it would likely help to implement
+a new object representation for large blobs as discussed in:
+
+https://lore.kernel.org/git/xmqqbkdometi.fsf@gitster.g/
+
+0) Non goals
+------------
+
+- We will not discuss those client side improvements here, as they
+ would require changes in different parts of Git than this effort.
++
+So we don't pretend to fully replace Git LFS with only this effort,
+but we nevertheless believe that it can significantly improve the
+current situation on the server side, and that other separate
+efforts could also improve the situation on the client side.
+
+- In the same way, we are not going to discuss all the possible ways
+ to implement a LOP or their underlying object storage, or to
+ optimize how LOP works.
++
+Our opinion is that the simplest solution for now is for LOPs to use
+object storage through a remote helper (see section II.2 below for
+more details) to store their objects. So we consider that this is the
+default implementation. If there are improvements on top of this,
+that's great, but our opinion is that such improvements are not
+necessary for LOPs to already be useful. Such improvements are likely
+a different technical topic, and can be taken care of separately
+anyway.
++
+So in particular we are not going to discuss pluggable ODBs or other
+object database backends that could chunk large blobs, dedup the
+chunks and store them efficiently. Sure, that would be a nice
+improvement to store large blobs on the server side, but we believe
+it can just be a separate effort as it's also not technically very
+related to this effort.
++
+We are also not going to discuss data transfer improvements between
+LOPs and clients or servers. Sure, there might be some easy and very
+effective optimizations there (as we know that objects on LOPs are
+very likely incompressible and not deltifying well), but this can be
+dealt with separately in a separate effort.
+
+In other words, the goal of this document is not to talk about all the
+possible ways to optimize how Git could handle large blobs, but to
+describe how a LOP based solution can already work well and alleviate
+a number of current issues in the context of Git clients and servers
+sharing Git objects.
+
+Even if LOPs are used not very efficiently, they can still be useful
+and worth using in some cases, as we will see in more details
+later in this document:
+
+ - they can make it simpler for clients to use promisor remotes and
+ therefore avoid fetching a lot of large blobs they might not need
+ locally,
+
+ - they can make it significantly cheaper or easier for servers to
+ host a significant part of the current repository content, and
+ even more to host content with larger blobs or more large blobs
+ than currently.
+
+I) Issues with the current situation
+------------------------------------
+
+- Some statistics made on GitLab repos have shown that more than 75%
+ of the disk space is used by blobs that are larger than 1MB and
+ often in a binary format.
+
+- So even if users could use Git LFS or similar tools to store a lot
+ of large blobs out of their repos, it's a fact that in practice they
+ don't do it as much as they probably should.
+
+- On the server side ideally, the server should be able to decide for
+ itself how it stores things. It should not depend on users deciding
+ to use tools like Git LFS on some blobs or not.
+
+- It's much more expensive to store large blobs that don't delta
+ compress well on regular fast seeking drives (like SSDs) than on
+ object storage (like Amazon S3 or GCP Buckets). Using fast drives
+ for regular Git repos makes sense though, as serving regular Git
+ content (blobs containing text or code) needs drives where seeking
+ is fast, but the content is relatively small. On the other hand,
+ object storage for Git LFS blobs makes sense as seeking speed is not
+ as important when dealing with large files, while costs are more
+ important. So the fact that users don't use Git LFS or similar tools
+ for a significant number of large blobs has likely some bad
+ consequences on the cost of repo storage for most Git hosting
+ platforms.
+
+- Having large blobs handled in the same way as other blobs and Git
+ objects in Git repos instead of on object storage also has a cost in
+ increased memory and CPU usage, and therefore decreased performance,
+ when creating packfiles. (This is because Git tries to use delta
+ compression or zlib compression which is unlikely to work well on
+ already compressed binary content.) So it's not just a storage cost
+ increase.
+
+- When a large blob has been committed into a repo, it might not be
+ possible to remove this blob from the repo without rewriting
+ history, even if the user then decides to use Git LFS or a similar
+ tool to handle it.
+
+- In fact Git LFS and similar tools are not very flexible in letting
+ users change their minds about the blobs they should handle or not.
+
+- Even when users are using Git LFS or similar tools, they are often
+ complaining that these tools require significant effort to set up,
+ learn and use correctly.
+
+II) Main features of the "Large Object Promisors" solution
+----------------------------------------------------------
+
+The main features below should give a rough overview of how the
+solution may work. Details about needed elements can be found in
+following sections.
+
+Even if each feature below is very useful for the full solution, it is
+very likely to be also useful on its own in some cases where the full
+solution is not required. However, we'll focus primarily on the big
+picture here.
+
+Also each feature doesn't need to be implemented entirely in Git
+itself. Some could be scripts, hooks or helpers that are not part of
+the Git repo. It would be helpful if those could be shared and
+improved on collaboratively though. So we want to encourage sharing
+them.
+
+1) Large blobs are stored on LOPs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Large blobs should be stored on special promisor remotes that we will
+call "Large Object Promisors" or LOPs. These LOPs should be additional
+remotes dedicated to contain large blobs especially those in binary
+format. They should be used along with main remotes that contain the
+other objects.
+
+Note 1
+++++++
+
+To clarify, a LOP is a normal promisor remote, except that:
+
+- it should store only large blobs,
+
+- it should be separate from the main remote, so that the main remote
+ can focus on serving other objects and the rest of the repos (see
+ feature 4) below) and can use the LOP as a promisor remote for
+ itself.
+
+Note 2
+++++++
+
+Git already makes it possible for a main remote to also be a promisor
+remote storing both regular objects and large blobs for a client that
+clones from it with a filter on blob size. But here we explicitly want
+to avoid that.
+
+Rationale
++++++++++
+
+LOPs aim to be good at handling large blobs while main remotes are
+already good at handling other objects.
+
+Implementation
+++++++++++++++
+
+Git already has support for multiple promisor remotes, see
+link:partial-clone.html#using-many-promisor-remotes[the partial clone documentation].
+
+Also, Git already has support for partial clone using a filter on the
+size of the blobs (with `git clone --filter=blob:limit=<size>`). Most
+of the other main features below are based on these existing features
+and are about making them easy and efficient to use for the purpose of
+better handling large blobs.
+
+2) LOPs can use object storage
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+LOPs can be implemented using object storage, like an Amazon S3 or GCP
+Bucket or MinIO (which is open source under the GNU AGPLv3 license) to
+actually store the large blobs, and can be accessed through a Git
+remote helper (see linkgit:gitremote-helpers[7]) which makes the
+underlying object storage appear like a remote to Git.
+
+Note
+++++
+
+A LOP can be a promisor remote accessed using a remote helper by
+both some clients and the main remote.
+
+Rationale
++++++++++
+
+This looks like the simplest way to create LOPs that can cheaply
+handle many large blobs.
+
+Implementation
+++++++++++++++
+
+Remote helpers are quite easy to write as shell scripts, but it might
+be more efficient and maintainable to write them using other languages
+like Go.
+
+Some already exist under open source licenses, for example:
+
+ - https://github.com/awslabs/git-remote-s3
+ - https://gitlab.com/eric.p.ju/git-remote-gs
+
+Other ways to implement LOPs are certainly possible, but the goal of
+this document is not to discuss how to best implement a LOP or its
+underlying object storage (see the "0) Non goals" section above).
+
+3) LOP object storage can be Git LFS storage
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The underlying object storage that a LOP uses could also serve as
+storage for large files handled by Git LFS.
+
+Rationale
++++++++++
+
+This would simplify the server side if it wants to both use a LOP and
+act as a Git LFS server.
+
+4) A main remote can offload to a LOP with a configurable threshold
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+On the server side, a main remote should have a way to offload to a
+LOP all its blobs with a size over a configurable threshold.
+
+Rationale
++++++++++
+
+This makes it easy to set things up and to clean things up. For
+example, an admin could use this to manually convert a repo not using
+LOPs to a repo using a LOP. On a repo already using a LOP but where
+some users would sometimes push large blobs, a cron job could use this
+to regularly make sure the large blobs are moved to the LOP.
+
+Implementation
+++++++++++++++
+
+Using something based on `git repack --filter=...` to separate the
+blobs we want to offload from the other Git objects could be a good
+idea. The missing part is to connect to the LOP, check if the blobs we
+want to offload are already there and if not send them.
+
+5) A main remote should try to remain clean from large blobs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A main remote should try to avoid containing a lot of oversize
+blobs. For that purpose, it should offload as needed to a LOP and it
+should have ways to prevent oversize blobs to be fetched, and also
+perhaps pushed, into it.
+
+Rationale
++++++++++
+
+A main remote containing many oversize blobs would defeat the purpose
+of LOPs.
+
+Implementation
+++++++++++++++
+
+The way to offload to a LOP discussed in 4) above can be used to
+regularly offload oversize blobs. About preventing oversize blobs from
+being fetched into the repo see 6) below. About preventing oversize
+blob pushes, a pre-receive hook could be used.
+
+Also there are different scenarios in which large blobs could get
+fetched into the main remote, for example:
+
+- A client that doesn't implement the "promisor-remote" protocol
+ (described in 6) below) clones from the main remote.
+
+- The main remote gets a request for information about a large blob
+ and is not able to get that information without fetching the blob
+ from the LOP.
+
+It might not be possible to completely prevent all these scenarios
+from happening. So the goal here should be to implement features that
+make the fetching of large blobs less likely. For example adding a
+`remote-object-info` command in the `git cat-file --batch` protocol
+and its variants might make it possible for a main repo to respond to
+some requests about large blobs without fetching them.
+
+6) A protocol negotiation should happen when a client clones
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When a client clones from a main repo, there should be a protocol
+negotiation so that the server can advertise one or more LOPs and so
+that the client and the server can discuss if the client could
+directly use a LOP the server is advertising. If the client and the
+server can agree on that, then the client would be able to get the
+large blobs directly from the LOP and the server would not need to
+fetch those blobs from the LOP to be able to serve the client.
+
+Note
+++++
+
+For fetches instead of clones, a protocol negotiation might not always
+happen, see the "What about fetches?" FAQ entry below for details.
+
+Rationale
++++++++++
+
+Security, configurability and efficiency of setting things up.
+
+Implementation
+++++++++++++++
+
+A "promisor-remote" protocol v2 capability looks like a good way to
+implement this. The way the client and server use this capability
+could be controlled by configuration variables.
+
+Information that the server could send to the client through that
+protocol could be things like: LOP name, LOP URL, filter-spec (for
+example `blob:limit=<size>`) or just size limit that should be used as
+a filter when cloning, token to be used with the LOP, etc.
+
+7) A client can offload to a LOP
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When a client is using a LOP that is also a LOP of its main remote,
+the client should be able to offload some large blobs it has fetched,
+but might not need anymore, to the LOP.
+
+Note
+++++
+
+It might depend on the context if it should be OK or not for clients
+to offload large blobs they have created, instead of fetched, directly
+to the LOP without the main remote checking them in some ways
+(possibly using hooks or other tools).
+
+This should be discussed and refined when we get closer to
+implementing this feature.
+
+Rationale
++++++++++
+
+On the client, the easiest way to deal with unneeded large blobs is to
+offload them.
+
+Implementation
+++++++++++++++
+
+This is very similar to what 4) above is about, except on the client
+side instead of the server side. So a good solution to 4) could likely
+be adapted to work on the client side too.
+
+There might be some security issues here, as there is no negotiation,
+but they might be mitigated if the client can reuse a token it got
+when cloning (see 6) above). Also if the large blobs were fetched from
+a LOP, it is likely, and can easily be confirmed, that the LOP still
+has them, so that they can just be removed from the client.
+
+III) Benefits of using LOPs
+---------------------------
+
+Many benefits are related to the issues discussed in "I) Issues with
+the current situation" above:
+
+- No need to rewrite history when deciding which blobs are worth
+ handling separately than other objects, or when moving or removing
+ the threshold.
+
+- If the protocol between client and server is developed and secured
+ enough, then many details might be setup on the server side only and
+ all the clients could then easily get all the configuration
+ information and use it to set themselves up mostly automatically.
+
+- Storage costs benefits on the server side.
+
+- Reduced memory and CPU needs on main remotes on the server side.
+
+- Reduced storage needs on the client side.
+
+IV) FAQ
+-------
+
+What about using multiple LOPs on the server and client side?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+That could perhaps be useful in some cases, but for now it's more
+likely that in most cases a single LOP will be advertised by the
+server and should be used by the client.
+
+A case where it could be useful for a server to advertise multiple
+LOPs is if a LOP is better for some users while a different LOP is
+better for other users. For example some clients might have a better
+connection to a LOP than others.
+
+In those cases it's the responsibility of the server to have some
+documentation to help clients. It could say for example something like
+"Users in this part of the world might want to pick only LOP A as it
+is likely to be better connected to them, while users in other parts
+of the world should pick only LOP B for the same reason."
+
+When should we trust or not trust the LOPs advertised by the server?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In some contexts, like in corporate setup where the server and all the
+clients are parts of an internal network in a company where admins
+have all the rights on every system, it's OK, and perhaps even a good
+thing, if the clients fully trust the server, as it can help ensure
+that all the clients are on the same page.
+
+There are also contexts in which clients trust a code hosting platform
+serving them some repos, but might not fully trust other users
+managing or contributing to some of these repos. For example, the code
+hosting platform could have hooks in place to check that any object it
+receives doesn't contain malware or otherwise bad content. In this
+case it might be OK for the client to use a main remote and its LOP if
+they are both hosted by the code hosting platform, but not if the LOP
+is hosted elsewhere (where the content is not checked).
+
+In other contexts, a client should just not trust a server.
+
+So there should be different ways to configure how the client should
+behave when a server advertises a LOP to it at clone time.
+
+As the basic elements that a server can advertise about a LOP are a
+LOP name and a LOP URL, the client should base its decision about
+accepting a LOP on these elements.
+
+One simple way to be very strict in the LOP it accepts is for example
+for the client to check that the LOP is already configured on the
+client with the same name and URL as what the server advertises.
+
+In general default and "safe" settings should require that the LOP are
+configured on the client separately from the "promisor-remote"
+protocol and that the client accepts a LOP only when information about
+it from the protocol matches what has been already configured
+separately.
+
+What about LOP names?
+~~~~~~~~~~~~~~~~~~~~~
+
+In some contexts, for example if the clients sometimes fetch from each
+other, it can be a good idea for all the clients to use the same names
+for all the remotes they use, including LOPs.
+
+In other contexts, each client might want to be able to give the name
+it wants to each remote, including each LOP, it interacts with.
+
+So there should be different ways to configure how the client accepts
+or not the LOP name the server advertises.
+
+If a default or "safe" setting is used, then as such a setting should
+require that the LOP be configured separately, then the name would be
+configured separately and there is no risk that the server could
+dictate a name to a client.
+
+Could the main remote be bogged down by old or paranoid clients?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Yes, it could happen if there are too many clients that are either
+unwilling to trust the main remote or that just don't implement the
+"promisor-remote" protocol because they are too old or not fully
+compatible with the 'git' client.
+
+When serving such a client, the main remote has no other choice than
+to first fetch from its LOP, to then be able to provide to the client
+everything it requested. So the main remote, even if it has cleanup
+mechanisms (see section II.4 above), would be burdened at least
+temporarily with the large blobs it had to fetch from its LOP.
+
+Not behaving like this would be breaking backward compatibility, and
+could be seen as segregating clients. For example, it might be
+possible to implement a special mode that allows the server to just
+reject clients that don't implement the "promisor-remote" protocol or
+aren't willing to trust the main remote. This mode might be useful in
+a special context like a corporate environment. There is no plan to
+implement such a mode though, and this should be discussed separately
+later anyway.
+
+A better way to proceed is probably for the main remote to show a
+message telling clients that don't implement the protocol or are
+unwilling to accept the advertised LOP(s) that they would get faster
+clone and fetches by upgrading client software or properly setting
+them up to accept LOP(s).
+
+Waiting for clients to upgrade, monitoring these upgrades and limiting
+the use of LOPs to repos that are not very frequently accessed might
+be other good ways to make sure that some benefits are still reaped
+from LOPs. Over time, as more and more clients upgrade and benefit
+from LOPs, using them in more and more frequently accessed repos will
+become worth it.
+
+Corporate environments, where it might be easier to make sure that all
+the clients are up-to-date and properly configured, could hopefully
+benefit more and earlier from using LOPs.
+
+What about fetches?
+~~~~~~~~~~~~~~~~~~~
+
+There are different kinds of fetches. A regular fetch happens when
+some refs have been updated on the server and the client wants the ref
+updates and possibly the new objects added with them. A "backfill" or
+"lazy" fetch, on the contrary, happens when the client needs to use
+some objects it already knows about but doesn't have because they are
+on a promisor remote.
+
+Regular fetch
++++++++++++++
+
+In a regular fetch, the client will contact the main remote and a
+protocol negotiation will happen between them. It's a good thing that
+a protocol negotiation happens every time, as the configuration on the
+client or the main remote could have changed since the previous
+protocol negotiation. In this case, the new protocol negotiation
+should ensure that the new fetch will happen in a way that satisfies
+the new configuration of both the client and the server.
+
+In most cases though, the configurations on the client and the main
+remote will not have changed between 2 fetches or between the initial
+clone and a subsequent fetch. This means that the result of a new
+protocol negotiation will be the same as the previous result, so the
+new fetch will happen in the same way as the previous clone or fetch,
+using, or not using, the same LOP(s) as last time.
+
+"Backfill" or "lazy" fetch
+++++++++++++++++++++++++++
+
+When there is a backfill fetch, the client doesn't necessarily contact
+the main remote first. It will try to fetch from its promisor remotes
+in the order they appear in the config file, except that a remote
+configured using the `extensions.partialClone` config variable will be
+tried last. See
+link:partial-clone.html#using-many-promisor-remotes[the partial clone documentation].
+
+This is not new with this effort. In fact this is how multiple remotes
+have already been working for around 5 years.
+
+When using LOPs, having the main remote configured using
+`extensions.partialClone`, so it's tried last, makes sense, as missing
+objects should only be large blobs that are on LOPs.
+
+This means that a protocol negotiation will likely not happen as the
+missing objects will be fetched from the LOPs, and then there will be
+nothing left to fetch from the main remote.
+
+To secure that, it could be a good idea for LOPs to require a token
+from the client when it fetches from them. The client could get the
+token when performing a protocol negotiation with the main remote (see
+section II.6 above).
+
+V) Future improvements
+----------------------
+
+It is expected that at the beginning using LOPs will be mostly worth
+it either in a corporate context where the Git version that clients
+use can easily be controlled, or on repos that are infrequently
+accessed. (See the "Could the main remote be bogged down by old or
+paranoid clients?" section in the FAQ above.)
+
+Over time, as more and more clients upgrade to a version that
+implements the "promisor-remote" protocol v2 capability described
+above in section II.6), it will be worth it to use LOPs more widely.
+
+A lot of improvements may also help using LOPs more widely. Some of
+these improvements are part of the scope of this document like the
+following:
+
+ - Implementing a "remote-object-info" command in the
+ `git cat-file --batch` protocol and its variants to allow main
+ remotes to respond to requests about large blobs without fetching
+ them. (Eric Ju has started working on this based on previous work
+ by Calvin Wan.)
+
+ - Creating better cleanup and offload mechanisms for main remotes
+ and clients to prevent accumulation of large blobs.
+
+ - Developing more sophisticated protocol negotiation capabilities
+ between clients and servers for handling LOPs, for example adding
+ a filter-spec (e.g., blob:limit=<size>) or size limit for
+ filtering when cloning, or adding a token for LOP authentication.
+
+ - Improving security measures for LOP access, particularly around
+ token handling and authentication.
+
+ - Developing standardized ways to configure and manage multiple LOPs
+ across different environments. Especially in the case where
+ different LOPs serve the same content to clients in different
+ geographical locations, there is a need for replication or
+ synchronization between LOPs.
+
+Some improvements, including some that have been mentioned in the "0)
+Non Goals" section of this document, are out of the scope of this
+document:
+
+ - Implementing a new object representation for large blobs on the
+ client side.
+
+ - Developing pluggable ODBs or other object database backends that
+ could chunk large blobs, dedup the chunks and store them
+ efficiently.
+
+ - Optimizing data transfer between LOPs and clients/servers,
+ particularly for incompressible and non-deltifying content.
+
+ - Creating improved client side tools for managing large objects
+ more effectively, for example tools for migrating from Git LFS or
+ git-annex, or tools to find which objects could be offloaded and
+ how much disk space could be reclaimed by offloading them.
+
+Some improvements could be seen as part of the scope of this document,
+but might already have their own separate projects from the Git
+project, like:
+
+ - Improving existing remote helpers to access object storage or
+ developing new ones.
+
+ - Improving existing object storage solutions or developing new
+ ones.
+
+Even though all the above improvements may help, this document and the
+LOP effort should try to focus, at least first, on a relatively small
+number of improvements mostly those that are in its current scope.
+
+For example introducing pluggable ODBs and a new object database
+backend is likely a multi-year effort on its own that can happen
+separately in parallel. It has different technical requirements,
+touches other part of the Git code base and should have its own design
+document(s).
diff --git a/Documentation/technical/meson.build b/Documentation/technical/meson.build
index 3a65ee59b3..a13aafcfbb 100644
--- a/Documentation/technical/meson.build
+++ b/Documentation/technical/meson.build
@@ -1,37 +1,37 @@
api_docs = [
- 'api-error-handling.txt',
- 'api-merge.txt',
- 'api-parse-options.txt',
- 'api-simple-ipc.txt',
- 'api-trace2.txt',
+ 'api-error-handling.adoc',
+ 'api-merge.adoc',
+ 'api-parse-options.adoc',
+ 'api-simple-ipc.adoc',
+ 'api-trace2.adoc',
]
articles = [
- 'bitmap-format.txt',
- 'build-systems.txt',
- 'bundle-uri.txt',
- 'commit-graph.txt',
- 'directory-rename-detection.txt',
- 'hash-function-transition.txt',
- 'long-running-process-protocol.txt',
- 'multi-pack-index.txt',
- 'packfile-uri.txt',
- 'pack-heuristics.txt',
- 'parallel-checkout.txt',
- 'partial-clone.txt',
- 'platform-support.txt',
- 'racy-git.txt',
- 'reftable.txt',
- 'remembering-renames.txt',
- 'repository-version.txt',
- 'rerere.txt',
- 'scalar.txt',
- 'send-pack-pipeline.txt',
- 'shallow.txt',
- 'sparse-checkout.txt',
- 'sparse-index.txt',
- 'trivial-merge.txt',
- 'unit-tests.txt',
+ 'bitmap-format.adoc',
+ 'build-systems.adoc',
+ 'bundle-uri.adoc',
+ 'commit-graph.adoc',
+ 'directory-rename-detection.adoc',
+ 'hash-function-transition.adoc',
+ 'long-running-process-protocol.adoc',
+ 'multi-pack-index.adoc',
+ 'packfile-uri.adoc',
+ 'pack-heuristics.adoc',
+ 'parallel-checkout.adoc',
+ 'partial-clone.adoc',
+ 'platform-support.adoc',
+ 'racy-git.adoc',
+ 'reftable.adoc',
+ 'remembering-renames.adoc',
+ 'repository-version.adoc',
+ 'rerere.adoc',
+ 'scalar.adoc',
+ 'send-pack-pipeline.adoc',
+ 'shallow.adoc',
+ 'sparse-checkout.adoc',
+ 'sparse-index.adoc',
+ 'trivial-merge.adoc',
+ 'unit-tests.adoc',
]
api_index = custom_target(
@@ -43,7 +43,7 @@ api_index = custom_target(
],
env: script_environment,
input: api_docs,
- output: 'api-index.txt',
+ output: 'api-index.adoc',
)
custom_target(
diff --git a/Documentation/technical/multi-pack-index.adoc b/Documentation/technical/multi-pack-index.adoc
index cc063b30be..ffda70aa13 100644
--- a/Documentation/technical/multi-pack-index.adoc
+++ b/Documentation/technical/multi-pack-index.adoc
@@ -164,19 +164,81 @@ objects_nr($H2) + objects_nr($H1) + i
(in the C implementation, this is often computed as `i +
m->num_objects_in_base`).
+=== Pseudo-pack order for incremental MIDXs
+
+The original implementation of multi-pack reachability bitmaps defined
+the pseudo-pack order in linkgit:gitformat-pack[5] (see the section
+titled "multi-pack-index reverse indexes") roughly as follows:
+
+____
+In short, a MIDX's pseudo-pack is the de-duplicated concatenation of
+objects in packs stored by the MIDX, laid out in pack order, and the
+packs arranged in MIDX order (with the preferred pack coming first).
+____
+
+In the incremental MIDX design, we extend this definition to include
+objects from multiple layers of the MIDX chain. The pseudo-pack order
+for incremental MIDXs is determined by concatenating the pseudo-pack
+ordering for each layer of the MIDX chain in order. Formally two objects
+`o1` and `o2` are compared as follows:
+
+1. If `o1` appears in an earlier layer of the MIDX chain than `o2`, then
+ `o1` sorts ahead of `o2`.
+
+2. Otherwise, if `o1` and `o2` appear in the same MIDX layer, and that
+ MIDX layer has no base, then if one of `pack(o1)` and `pack(o2)` is
+ preferred and the other is not, then the preferred one sorts ahead of
+ the non-preferred one. If there is a base layer (i.e. the MIDX layer
+ is not the first layer in the chain), then if `pack(o1)` appears
+ earlier in that MIDX layer's pack order, then `o1` sorts ahead of
+ `o2`. Likewise if `pack(o2)` appears earlier, then the opposite is
+ true.
+
+3. Otherwise, `o1` and `o2` appear in the same pack, and thus in the
+ same MIDX layer. Sort `o1` and `o2` by their offset within their
+ containing packfile.
+
+Note that the preferred pack is a property of the MIDX chain, not the
+individual layers themselves. Fundamentally we could introduce a
+per-layer preferred pack, but this is less relevant now that we can
+perform multi-pack reuse across the set of packs in a MIDX.
+
+=== Reachability bitmaps and incremental MIDXs
+
+Each layer of an incremental MIDX chain may have its objects (and the
+objects from any previous layer in the same MIDX chain) represented in
+its own `*.bitmap` file.
+
+The structure of a `*.bitmap` file belonging to an incremental MIDX
+chain is identical to that of a non-incremental MIDX bitmap, or a
+classic single-pack bitmap. Since objects are added to the end of the
+incremental MIDX's pseudo-pack order (see above), it is possible to
+extend a bitmap when appending to the end of a MIDX chain.
+
+(Note: it is possible likewise to compress a contiguous sequence of MIDX
+incremental layers, and their `*.bitmap` files into a single layer and
+`*.bitmap`, but this is not yet implemented.)
+
+The object positions used are global within the pseudo-pack order, so
+subsequent layers will have, for example, `m->num_objects_in_base`
+number of `0` bits in each of their four type bitmaps. This follows from
+the fact that we only write type bitmap entries for objects present in
+the layer immediately corresponding to the bitmap).
+
+Note also that only the bitmap pertaining to the most recent layer in an
+incremental MIDX chain is used to store reachability information about
+the interesting and uninteresting objects in a reachability query.
+Earlier bitmap layers are only used to look up commit and pseudo-merge
+bitmaps from that layer, as well as the type-level bitmaps for objects
+in that layer.
+
+To simplify the implementation, type-level bitmaps are iterated
+simultaneously, and their results are OR'd together to avoid recursively
+calling internal bitmap functions.
+
Future Work
-----------
-- The multi-pack-index allows many packfiles, especially in a context
- where repacking is expensive (such as a very large repo), or
- unexpected maintenance time is unacceptable (such as a high-demand
- build machine). However, the multi-pack-index needs to be rewritten
- in full every time. We can extend the format to be incremental, so
- writes are fast. By storing a small "tip" multi-pack-index that
- points to large "base" MIDX files, we can keep writes fast while
- still reducing the number of binary searches required for object
- lookups.
-
- If the multi-pack-index is extended to store a "stable object order"
(a function Order(hash) = integer that is constant for a given hash,
even as the multi-pack-index is updated) then MIDX bitmaps could be
diff --git a/Documentation/technical/partial-clone.adoc b/Documentation/technical/partial-clone.adoc
index bf5ec5c82d..e513e391ea 100644
--- a/Documentation/technical/partial-clone.adoc
+++ b/Documentation/technical/partial-clone.adoc
@@ -85,7 +85,7 @@ See "filter" in linkgit:gitprotocol-pack[5].
server to request filtering during packfile construction.
+
There are various filters available to accommodate different situations.
-See "--filter=<filter-spec>" in Documentation/rev-list-options.txt.
+See "--filter=<filter-spec>" in Documentation/rev-list-options.adoc.
- On the server pack-objects applies the requested filter-spec as it
creates "filtered" packfiles for the client.
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 7fb7fff6dd..b981598298 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,6 +1,6 @@
#!/bin/sh
-DEF_VER=v2.49.0-rc1
+DEF_VER=v2.49.GIT
LF='
'
diff --git a/Makefile b/Makefile
index a9b2de0692..2e73a82a44 100644
--- a/Makefile
+++ b/Makefile
@@ -194,7 +194,7 @@ include shared.mak
# Linux, kernel 2.6.11 or newer is required for reliable sub-second file times
# on file systems with exactly 1 ns or 1 s resolution. If you intend to use Git
# on other file systems (e.g. CEPH, CIFS, NTFS, UDF), don't enable USE_NSEC. See
-# Documentation/technical/racy-git.txt for details.
+# Documentation/technical/racy-git.adoc for details.
#
# Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of
# "st_ctim"
@@ -955,7 +955,7 @@ FOUND_SOURCE_FILES := $(filter-out $(GENERATED_H),$(shell $(SOURCES_CMD)))
FOUND_C_SOURCES = $(filter %.c,$(FOUND_SOURCE_FILES))
FOUND_H_SOURCES = $(filter %.h,$(FOUND_SOURCE_FILES))
-COCCI_SOURCES = $(filter-out $(THIRD_PARTY_SOURCES),$(FOUND_C_SOURCES))
+COCCI_SOURCES = $(filter-out $(THIRD_PARTY_SOURCES) reftable/%,$(FOUND_C_SOURCES))
LIB_H = $(FOUND_H_SOURCES)
@@ -995,6 +995,7 @@ LIB_OBJS += common-init.o
LIB_OBJS += compat/nonblock.o
LIB_OBJS += compat/obstack.o
LIB_OBJS += compat/terminal.o
+LIB_OBJS += compiler-tricks/not-constant.o
LIB_OBJS += config.o
LIB_OBJS += connect.o
LIB_OBJS += connected.o
@@ -1242,6 +1243,7 @@ BUILTIN_OBJS += builtin/describe.o
BUILTIN_OBJS += builtin/diagnose.o
BUILTIN_OBJS += builtin/diff-files.o
BUILTIN_OBJS += builtin/diff-index.o
+BUILTIN_OBJS += builtin/diff-pairs.o
BUILTIN_OBJS += builtin/diff-tree.o
BUILTIN_OBJS += builtin/diff.o
BUILTIN_OBJS += builtin/difftool.o
@@ -1356,6 +1358,9 @@ CLAR_TEST_SUITES += u-example-decorate
CLAR_TEST_SUITES += u-hash
CLAR_TEST_SUITES += u-hashmap
CLAR_TEST_SUITES += u-mem-pool
+CLAR_TEST_SUITES += u-oid-array
+CLAR_TEST_SUITES += u-oidmap
+CLAR_TEST_SUITES += u-oidtree
CLAR_TEST_SUITES += u-prio-queue
CLAR_TEST_SUITES += u-reftable-tree
CLAR_TEST_SUITES += u-strbuf
@@ -1365,10 +1370,8 @@ CLAR_TEST_PROG = $(UNIT_TEST_BIN)/unit-tests$(X)
CLAR_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(CLAR_TEST_SUITES))
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o
CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o
+CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
-UNIT_TEST_PROGRAMS += t-oid-array
-UNIT_TEST_PROGRAMS += t-oidmap
-UNIT_TEST_PROGRAMS += t-oidtree
UNIT_TEST_PROGRAMS += t-reftable-basics
UNIT_TEST_PROGRAMS += t-reftable-block
UNIT_TEST_PROGRAMS += t-reftable-merged
@@ -1381,7 +1384,6 @@ UNIT_TEST_PROGRAMS += t-trailer
UNIT_TEST_PROGRAMS += t-urlmatch-normalization
UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
-UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o
UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o
# xdiff and reftable libs may in turn depend on what is in libgit.a
@@ -2261,6 +2263,10 @@ ifdef WITH_BREAKING_CHANGES
BASIC_CFLAGS += -DWITH_BREAKING_CHANGES
endif
+ifdef CHECK_ASSERTION_SIDE_EFFECTS
+ BASIC_CFLAGS += -DCHECK_ASSERTION_SIDE_EFFECTS
+endif
+
ifdef INCLUDE_LIBGIT_RS
# Enable symbol hiding in contrib/libgit-sys/libgitpub.a without making
# us rebuild the whole tree every time we run a Rust build.
diff --git a/README.md b/README.md
index 665ce5f5a8..d87bca1b8c 100644
--- a/README.md
+++ b/README.md
@@ -17,15 +17,15 @@ Please read the file [INSTALL][] for installation instructions.
Many Git online resources are accessible from <https://git-scm.com/>
including full documentation and Git related tools.
-See [Documentation/gittutorial.txt][] to get started, then see
-[Documentation/giteveryday.txt][] for a useful minimum set of commands, and
-`Documentation/git-<commandname>.txt` for documentation of each command.
+See [Documentation/gittutorial.adoc][] to get started, then see
+[Documentation/giteveryday.adoc][] for a useful minimum set of commands, and
+`Documentation/git-<commandname>.adoc` for documentation of each command.
If git has been correctly installed, then the tutorial can also be
read with `man gittutorial` or `git help tutorial`, and the
documentation of each command with `man git-<commandname>` or `git help
<commandname>`.
-CVS users may also want to read [Documentation/gitcvs-migration.txt][]
+CVS users may also want to read [Documentation/gitcvs-migration.adoc][]
(`man gitcvs-migration` or `git help cvs-migration` if git is
installed).
@@ -66,9 +66,9 @@ and the name as (depending on your mood):
- "goddamn idiotic truckload of sh*t": when it breaks
[INSTALL]: INSTALL
-[Documentation/gittutorial.txt]: Documentation/gittutorial.txt
-[Documentation/giteveryday.txt]: Documentation/giteveryday.txt
-[Documentation/gitcvs-migration.txt]: Documentation/gitcvs-migration.txt
+[Documentation/gittutorial.adoc]: Documentation/gittutorial.adoc
+[Documentation/giteveryday.adoc]: Documentation/giteveryday.adoc
+[Documentation/gitcvs-migration.adoc]: Documentation/gitcvs-migration.adoc
[Documentation/SubmittingPatches]: Documentation/SubmittingPatches
[Documentation/CodingGuidelines]: Documentation/CodingGuidelines
[po/README.md]: po/README.md
diff --git a/RelNotes b/RelNotes
index ac72bdf04d..eaaaf878d3 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.49.0.adoc \ No newline at end of file
+Documentation/RelNotes/2.50.0.adoc \ No newline at end of file
diff --git a/advice.h b/advice.h
index cf2284ec43..d233cfc693 100644
--- a/advice.h
+++ b/advice.h
@@ -7,7 +7,7 @@ struct string_list;
* To add a new advice, you need to:
* Define a new advice_type.
* Add a new entry to advice_setting array.
- * Add the new config variable to Documentation/config/advice.txt.
+ * Add the new config variable to Documentation/config/advice.adoc.
* Call advise_if_enabled to print your advice.
*/
enum advice_type {
diff --git a/apply.c b/apply.c
index b124678b93..f274a37948 100644
--- a/apply.c
+++ b/apply.c
@@ -82,7 +82,7 @@ static int parse_whitespace_option(struct apply_state *state, const char *option
}
/*
* Please update $__git_whitespacelist in git-completion.bash,
- * Documentation/git-apply.txt, and Documentation/git-am.txt
+ * Documentation/git-apply.adoc, and Documentation/git-am.adoc
* when you add new options.
*/
return error(_("unrecognized whitespace option '%s'"), option);
diff --git a/bisect.c b/bisect.c
index 7a3c77c6d8..269a98bf97 100644
--- a/bisect.c
+++ b/bisect.c
@@ -930,7 +930,7 @@ static enum bisect_error check_good_are_ancestors_of_bad(struct repository *r,
if (!current_bad_oid)
return error(_("a %s revision is needed"), term_bad);
- filename = git_pathdup("BISECT_ANCESTORS_OK");
+ filename = repo_git_path(the_repository, "BISECT_ANCESTORS_OK");
/* Check if file BISECT_ANCESTORS_OK exists. */
if (!stat(filename, &st) && S_ISREG(st.st_mode))
diff --git a/branch.c b/branch.c
index 77716966fe..91297d55ac 100644
--- a/branch.c
+++ b/branch.c
@@ -397,7 +397,7 @@ static void prepare_checked_out_branches(void)
worktrees = get_worktrees();
while (worktrees[i]) {
- char *old;
+ char *old, *wt_gitdir;
struct wt_status_state state = { 0 };
struct worktree *wt = worktrees[i++];
struct string_list update_refs = STRING_LIST_INIT_DUP;
@@ -437,7 +437,8 @@ static void prepare_checked_out_branches(void)
}
wt_status_state_free_buffers(&state);
- if (!sequencer_get_update_refs_state(get_worktree_git_dir(wt),
+ wt_gitdir = get_worktree_git_dir(wt);
+ if (!sequencer_get_update_refs_state(wt_gitdir,
&update_refs)) {
struct string_list_item *item;
for_each_string_list_item(item, &update_refs) {
@@ -448,6 +449,8 @@ static void prepare_checked_out_branches(void)
}
string_list_clear(&update_refs, 1);
}
+
+ free(wt_gitdir);
}
free_worktrees(worktrees);
diff --git a/builtin.h b/builtin.h
index 89928ccf92..bff13e3069 100644
--- a/builtin.h
+++ b/builtin.h
@@ -63,7 +63,7 @@
*
* . Add tests to `t/` directory.
*
- * . Write documentation in `Documentation/git-foo.txt`.
+ * . Write documentation in `Documentation/git-foo.adoc`.
*
* . Add an entry for `git-foo` to `command-list.txt`.
*
@@ -153,6 +153,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix, struct reposit
int cmd_diff_files(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_diff_index(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_diff(int argc, const char **argv, const char *prefix, struct repository *repo);
+int cmd_diff_pairs(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_diff_tree(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_difftool(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_env__helper(int argc, const char **argv, const char *prefix, struct repository *repo);
diff --git a/builtin/am.c b/builtin/am.c
index 390b463144..3b61bd4c33 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -31,7 +31,7 @@
#include "preload-index.h"
#include "sequencer.h"
#include "revision.h"
-#include "merge-recursive.h"
+#include "merge-ort-wrappers.h"
#include "log-tree.h"
#include "notes-utils.h"
#include "rerere.h"
@@ -158,7 +158,7 @@ static void am_state_init(struct am_state *state)
memset(state, 0, sizeof(*state));
- state->dir = git_pathdup("rebase-apply");
+ state->dir = repo_git_path(the_repository, "rebase-apply");
state->prec = 4;
@@ -1638,12 +1638,13 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
o.branch1 = "HEAD";
their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
o.branch2 = their_tree_name;
+ o.ancestor = "constructed fake ancestor";
o.detect_directory_renames = MERGE_DIRECTORY_RENAMES_NONE;
if (state->quiet)
o.verbosity = 0;
- if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
+ if (merge_ort_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
repo_rerere(the_repository, state->allow_rerere_autoupdate);
free(their_tree_name);
return error(_("Failed to merge in the changes."));
diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c
index e30086c7d4..7f74bc702f 100644
--- a/builtin/checkout-index.c
+++ b/builtin/checkout-index.c
@@ -5,7 +5,6 @@
*
*/
-#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "builtin.h"
@@ -68,10 +67,10 @@ static void write_tempfile_record(const char *name, const char *prefix)
}
}
-static int checkout_file(const char *name, const char *prefix)
+static int checkout_file(struct index_state *index, const char *name, const char *prefix)
{
int namelen = strlen(name);
- int pos = index_name_pos(the_repository->index, name, namelen);
+ int pos = index_name_pos(index, name, namelen);
int has_same_name = 0;
int is_file = 0;
int is_skipped = 1;
@@ -81,8 +80,8 @@ static int checkout_file(const char *name, const char *prefix)
if (pos < 0)
pos = -pos - 1;
- while (pos <the_repository->index->cache_nr) {
- struct cache_entry *ce =the_repository->index->cache[pos];
+ while (pos < index->cache_nr) {
+ struct cache_entry *ce = index->cache[pos];
if (ce_namelen(ce) != namelen ||
memcmp(ce->name, name, namelen))
break;
@@ -137,13 +136,13 @@ static int checkout_file(const char *name, const char *prefix)
return -1;
}
-static int checkout_all(const char *prefix, int prefix_length)
+static int checkout_all(struct index_state *index, const char *prefix, int prefix_length)
{
int i, errs = 0;
struct cache_entry *last_ce = NULL;
- for (i = 0; i < the_repository->index->cache_nr ; i++) {
- struct cache_entry *ce = the_repository->index->cache[i];
+ for (i = 0; i < index->cache_nr ; i++) {
+ struct cache_entry *ce = index->cache[i];
if (S_ISSPARSEDIR(ce->ce_mode)) {
if (!ce_skip_worktree(ce))
@@ -156,8 +155,8 @@ static int checkout_all(const char *prefix, int prefix_length)
* first entry inside the expanded sparse directory).
*/
if (ignore_skip_worktree) {
- ensure_full_index(the_repository->index);
- ce = the_repository->index->cache[i];
+ ensure_full_index(index);
+ ce = index->cache[i];
}
}
@@ -213,7 +212,7 @@ static int option_parse_stage(const struct option *opt,
int cmd_checkout_index(int argc,
const char **argv,
const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
int i;
struct lock_file lock_file = LOCK_INIT;
@@ -253,19 +252,19 @@ int cmd_checkout_index(int argc,
show_usage_with_options_if_asked(argc, argv,
builtin_checkout_index_usage,
builtin_checkout_index_options);
- git_config(git_default_config, NULL);
+ repo_config(repo, git_default_config, NULL);
prefix_length = prefix ? strlen(prefix) : 0;
- prepare_repo_settings(the_repository);
- the_repository->settings.command_requires_full_index = 0;
+ prepare_repo_settings(repo);
+ repo->settings.command_requires_full_index = 0;
- if (repo_read_index(the_repository) < 0) {
+ if (repo_read_index(repo) < 0) {
die("invalid cache");
}
argc = parse_options(argc, argv, prefix, builtin_checkout_index_options,
builtin_checkout_index_usage, 0);
- state.istate = the_repository->index;
+ state.istate = repo->index;
state.force = force;
state.quiet = quiet;
state.not_new = not_new;
@@ -285,8 +284,8 @@ int cmd_checkout_index(int argc,
*/
if (index_opt && !state.base_dir_len && !to_tempfile) {
state.refresh_cache = 1;
- state.istate = the_repository->index;
- repo_hold_locked_index(the_repository, &lock_file,
+ state.istate = repo->index;
+ repo_hold_locked_index(repo, &lock_file,
LOCK_DIE_ON_ERROR);
}
@@ -304,7 +303,7 @@ int cmd_checkout_index(int argc,
if (read_from_stdin)
die("git checkout-index: don't mix '--stdin' and explicit filenames");
p = prefix_path(prefix, prefix_length, arg);
- err |= checkout_file(p, prefix);
+ err |= checkout_file(repo->index, p, prefix);
free(p);
}
@@ -326,7 +325,7 @@ int cmd_checkout_index(int argc,
strbuf_swap(&buf, &unquoted);
}
p = prefix_path(prefix, prefix_length, buf.buf);
- err |= checkout_file(p, prefix);
+ err |= checkout_file(repo->index, p, prefix);
free(p);
}
strbuf_release(&unquoted);
@@ -334,7 +333,7 @@ int cmd_checkout_index(int argc,
}
if (all)
- err |= checkout_all(prefix, prefix_length);
+ err |= checkout_all(repo->index, prefix, prefix_length);
if (pc_workers > 1)
err |= run_parallel_checkout(&state, pc_workers, pc_threshold,
@@ -344,7 +343,7 @@ int cmd_checkout_index(int argc,
return 1;
if (is_lock_file_locked(&lock_file) &&
- write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
+ write_locked_index(repo->index, &lock_file, COMMIT_LOCK))
die("Unable to write new index file");
return 0;
}
diff --git a/builtin/clone.c b/builtin/clone.c
index f9a2ecbe9c..88276e5b7a 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -342,6 +342,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
strbuf_setlen(src, src_len);
die(_("failed to iterate over '%s'"), src->buf);
}
+
+ dir_iterator_free(iter);
}
static void clone_local(const char *src_repo, const char *dest_repo)
@@ -835,7 +837,7 @@ static void write_refspec_config(const char *src_ref_prefix,
static void dissociate_from_references(void)
{
- char *alternates = git_pathdup("objects/info/alternates");
+ char *alternates = repo_git_path(the_repository, "objects/info/alternates");
if (!access(alternates, F_OK)) {
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -1219,7 +1221,7 @@ int cmd_clone(int argc,
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/refs", git_dir);
- safe_create_dir(buf.buf, 1);
+ safe_create_dir(the_repository, buf.buf, 1);
/*
* additional config can be injected with -c, make sure it's included
diff --git a/builtin/commit.c b/builtin/commit.c
index 9fb405dd4a..2f45968222 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -352,6 +352,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
struct pathspec pathspec;
int refresh_flags = REFRESH_QUIET;
const char *ret;
+ char *path = NULL;
if (is_status)
refresh_flags |= REFRESH_UNMERGED;
@@ -524,9 +525,9 @@ static const char *prepare_index(const char **argv, const char *prefix,
if (write_locked_index(the_repository->index, &index_lock, 0))
die(_("unable to write new index file"));
- hold_lock_file_for_update(&false_lock,
- git_path("next-index-%"PRIuMAX,
- (uintmax_t) getpid()),
+ path = repo_git_path(the_repository, "next-index-%"PRIuMAX,
+ (uintmax_t) getpid());
+ hold_lock_file_for_update(&false_lock, path,
LOCK_DIE_ON_ERROR);
create_base_index(current_head);
@@ -542,6 +543,7 @@ static const char *prepare_index(const char **argv, const char *prefix,
out:
string_list_clear(&partial, 0);
clear_pathspec(&pathspec);
+ free(path);
return ret;
}
diff --git a/builtin/config.c b/builtin/config.c
index 16e6e30555..53a90094e3 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -775,13 +775,13 @@ static void location_options_init(struct config_location_options *opts,
opts->source.file = opts->file_to_free = git_system_config();
opts->source.scope = CONFIG_SCOPE_SYSTEM;
} else if (opts->use_local_config) {
- opts->source.file = opts->file_to_free = git_pathdup("config");
+ opts->source.file = opts->file_to_free = repo_git_path(the_repository, "config");
opts->source.scope = CONFIG_SCOPE_LOCAL;
} else if (opts->use_worktree_config) {
struct worktree **worktrees = get_worktrees();
if (the_repository->repository_format_worktree_config)
opts->source.file = opts->file_to_free =
- git_pathdup("config.worktree");
+ repo_git_path(the_repository, "config.worktree");
else if (worktrees[0] && worktrees[1])
die(_("--worktree cannot be used with multiple "
"working trees unless the config\n"
@@ -790,7 +790,7 @@ static void location_options_init(struct config_location_options *opts,
"section in \"git help worktree\" for details"));
else
opts->source.file = opts->file_to_free =
- git_pathdup("config");
+ repo_git_path(the_repository, "config");
opts->source.scope = CONFIG_SCOPE_LOCAL;
free_worktrees(worktrees);
} else if (opts->source.file) {
@@ -1087,7 +1087,7 @@ static int show_editor(struct config_location_options *opts)
git_config(git_default_config, NULL);
config_file = opts->source.file ?
xstrdup(opts->source.file) :
- git_pathdup("config");
+ repo_git_path(the_repository, "config");
if (opts->use_global_config) {
int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
if (fd >= 0) {
diff --git a/builtin/diff-pairs.c b/builtin/diff-pairs.c
new file mode 100644
index 0000000000..71c045331a
--- /dev/null
+++ b/builtin/diff-pairs.c
@@ -0,0 +1,207 @@
+#include "builtin.h"
+#include "config.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "gettext.h"
+#include "hash.h"
+#include "hex.h"
+#include "object.h"
+#include "parse-options.h"
+#include "revision.h"
+#include "strbuf.h"
+
+static unsigned parse_mode_or_die(const char *mode, const char **end)
+{
+ uint16_t ret;
+
+ *end = parse_mode(mode, &ret);
+ if (!*end)
+ die(_("unable to parse mode: %s"), mode);
+ return ret;
+}
+
+static void parse_oid_or_die(const char *hex, struct object_id *oid,
+ const char **end, const struct git_hash_algo *algop)
+{
+ if (parse_oid_hex_algop(hex, oid, end, algop) || *(*end)++ != ' ')
+ die(_("unable to parse object id: %s"), hex);
+}
+
+int cmd_diff_pairs(int argc, const char **argv, const char *prefix,
+ struct repository *repo)
+{
+ struct strbuf path_dst = STRBUF_INIT;
+ struct strbuf path = STRBUF_INIT;
+ struct strbuf meta = STRBUF_INIT;
+ struct option *parseopts;
+ struct rev_info revs;
+ int line_term = '\0';
+ int ret;
+
+ const char * const builtin_diff_pairs_usage[] = {
+ N_("git diff-pairs -z [<diff-options>]"),
+ NULL
+ };
+ struct option builtin_diff_pairs_options[] = {
+ OPT_END()
+ };
+
+ repo_init_revisions(repo, &revs, prefix);
+
+ /*
+ * Diff options are usually parsed implicitly as part of
+ * setup_revisions(). Explicitly handle parsing to ensure options are
+ * printed in the usage message.
+ */
+ parseopts = add_diff_options(builtin_diff_pairs_options, &revs.diffopt);
+ show_usage_with_options_if_asked(argc, argv, builtin_diff_pairs_usage, parseopts);
+
+ repo_config(repo, git_diff_basic_config, NULL);
+ revs.diffopt.no_free = 1;
+ revs.disable_stdin = 1;
+ revs.abbrev = 0;
+ revs.diff = 1;
+
+ argc = parse_options(argc, argv, prefix, parseopts, builtin_diff_pairs_usage,
+ PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_DASHDASH);
+
+ if (setup_revisions(argc, argv, &revs, NULL) > 1)
+ usagef(_("unrecognized argument: %s"), argv[0]);
+
+ /*
+ * With the -z option, both command input and raw output are
+ * NUL-delimited (this mode does not affect patch output). At present
+ * only NUL-delimited raw diff formatted input is supported.
+ */
+ if (revs.diffopt.line_termination)
+ usage(_("working without -z is not supported"));
+
+ if (revs.prune_data.nr)
+ usage(_("pathspec arguments not supported"));
+
+ if (revs.pending.nr || revs.max_count != -1 ||
+ revs.min_age != (timestamp_t)-1 ||
+ revs.max_age != (timestamp_t)-1)
+ usage(_("revision arguments not allowed"));
+
+ if (!revs.diffopt.output_format)
+ revs.diffopt.output_format = DIFF_FORMAT_PATCH;
+
+ /*
+ * If rename detection is not requested, use rename information from the
+ * raw diff formatted input. Setting skip_resolving_statuses ensures
+ * diffcore_std() does not mess with rename information already present
+ * in queued filepairs.
+ */
+ if (!revs.diffopt.detect_rename)
+ revs.diffopt.skip_resolving_statuses = 1;
+
+ while (1) {
+ struct object_id oid_a, oid_b;
+ struct diff_filepair *pair;
+ unsigned mode_a, mode_b;
+ const char *p;
+ char status;
+
+ if (strbuf_getwholeline(&meta, stdin, line_term) == EOF)
+ break;
+
+ p = meta.buf;
+ if (!*p) {
+ diffcore_std(&revs.diffopt);
+ diff_flush(&revs.diffopt);
+ /*
+ * When the diff queue is explicitly flushed, append a
+ * NUL byte to separate batches of diffs.
+ */
+ fputc('\0', revs.diffopt.file);
+ fflush(revs.diffopt.file);
+ continue;
+ }
+
+ if (*p != ':')
+ die(_("invalid raw diff input"));
+ p++;
+
+ mode_a = parse_mode_or_die(p, &p);
+ mode_b = parse_mode_or_die(p, &p);
+
+ if (S_ISDIR(mode_a) || S_ISDIR(mode_b))
+ die(_("tree objects not supported"));
+
+ parse_oid_or_die(p, &oid_a, &p, repo->hash_algo);
+ parse_oid_or_die(p, &oid_b, &p, repo->hash_algo);
+
+ status = *p++;
+
+ if (strbuf_getwholeline(&path, stdin, line_term) == EOF)
+ die(_("got EOF while reading path"));
+
+ switch (status) {
+ case DIFF_STATUS_ADDED:
+ pair = diff_queue_addremove(&diff_queued_diff,
+ &revs.diffopt, '+', mode_b,
+ &oid_b, 1, path.buf, 0);
+ if (pair)
+ pair->status = status;
+ break;
+
+ case DIFF_STATUS_DELETED:
+ pair = diff_queue_addremove(&diff_queued_diff,
+ &revs.diffopt, '-', mode_a,
+ &oid_a, 1, path.buf, 0);
+ if (pair)
+ pair->status = status;
+ break;
+
+ case DIFF_STATUS_TYPE_CHANGED:
+ case DIFF_STATUS_MODIFIED:
+ pair = diff_queue_change(&diff_queued_diff, &revs.diffopt,
+ mode_a, mode_b, &oid_a, &oid_b,
+ 1, 1, path.buf, 0, 0);
+ if (pair)
+ pair->status = status;
+ break;
+
+ case DIFF_STATUS_RENAMED:
+ case DIFF_STATUS_COPIED: {
+ struct diff_filespec *a, *b;
+ unsigned int score;
+
+ if (strbuf_getwholeline(&path_dst, stdin, line_term) == EOF)
+ die(_("got EOF while reading destination path"));
+
+ a = alloc_filespec(path.buf);
+ b = alloc_filespec(path_dst.buf);
+ fill_filespec(a, &oid_a, 1, mode_a);
+ fill_filespec(b, &oid_b, 1, mode_b);
+
+ pair = diff_queue(&diff_queued_diff, a, b);
+
+ if (strtoul_ui(p, 10, &score))
+ die(_("unable to parse rename/copy score: %s"), p);
+
+ pair->score = score * MAX_SCORE / 100;
+ pair->status = status;
+ pair->renamed_pair = 1;
+ }
+ break;
+
+ default:
+ die(_("unknown diff status: %c"), status);
+ }
+ }
+
+ revs.diffopt.no_free = 0;
+ diffcore_std(&revs.diffopt);
+ diff_flush(&revs.diffopt);
+ ret = diff_result_code(&revs);
+
+ strbuf_release(&path_dst);
+ strbuf_release(&path);
+ strbuf_release(&meta);
+ release_revisions(&revs);
+ FREE_AND_NULL(parseopts);
+
+ return ret;
+}
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index a5c82eef1d..126980f724 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -35,8 +35,11 @@ static const char *fast_export_usage[] = {
NULL
};
+enum sign_mode { SIGN_ABORT, SIGN_VERBATIM, SIGN_STRIP, SIGN_WARN_VERBATIM, SIGN_WARN_STRIP };
+
static int progress;
-static enum signed_tag_mode { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT;
+static enum sign_mode signed_tag_mode = SIGN_ABORT;
+static enum sign_mode signed_commit_mode = SIGN_ABORT;
static enum tag_of_filtered_mode { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT;
static enum reencode_mode { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT;
static int fake_missing_tagger;
@@ -53,23 +56,24 @@ static int anonymize;
static struct hashmap anonymized_seeds;
static struct revision_sources revision_sources;
-static int parse_opt_signed_tag_mode(const struct option *opt,
+static int parse_opt_sign_mode(const struct option *opt,
const char *arg, int unset)
{
- enum signed_tag_mode *val = opt->value;
-
- if (unset || !strcmp(arg, "abort"))
- *val = SIGNED_TAG_ABORT;
+ enum sign_mode *val = opt->value;
+ if (unset)
+ return 0;
+ else if (!strcmp(arg, "abort"))
+ *val = SIGN_ABORT;
else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore"))
- *val = VERBATIM;
- else if (!strcmp(arg, "warn"))
- *val = WARN;
+ *val = SIGN_VERBATIM;
+ else if (!strcmp(arg, "warn-verbatim") || !strcmp(arg, "warn"))
+ *val = SIGN_WARN_VERBATIM;
else if (!strcmp(arg, "warn-strip"))
- *val = WARN_STRIP;
+ *val = SIGN_WARN_STRIP;
else if (!strcmp(arg, "strip"))
- *val = STRIP;
+ *val = SIGN_STRIP;
else
- return error("Unknown signed-tags mode: %s", arg);
+ return error("Unknown %s mode: %s", opt->long_name, arg);
return 0;
}
@@ -510,21 +514,6 @@ static void show_filemodify(struct diff_queue_struct *q,
}
}
-static const char *find_encoding(const char *begin, const char *end)
-{
- const char *needle = "\nencoding ";
- char *bol, *eol;
-
- bol = memmem(begin, end ? end - begin : strlen(begin),
- needle, strlen(needle));
- if (!bol)
- return NULL;
- bol += strlen(needle);
- eol = strchrnul(bol, '\n');
- *eol = '\0';
- return bol;
-}
-
static char *anonymize_ref_component(void)
{
static int counter;
@@ -626,13 +615,53 @@ static void anonymize_ident_line(const char **beg, const char **end)
*end = out->buf + out->len;
}
+/*
+ * find_commit_multiline_header is similar to find_commit_header,
+ * except that it handles multi-line headers, rather than simply
+ * returning the first line of the header.
+ *
+ * The returned string has had the ' ' line continuation markers
+ * removed, and points to allocated memory that must be free()d (not
+ * to memory within 'msg').
+ *
+ * If the header is found, then *end is set to point at the '\n' in
+ * msg that immediately follows the header value.
+ */
+static const char *find_commit_multiline_header(const char *msg,
+ const char *key,
+ const char **end)
+{
+ struct strbuf val = STRBUF_INIT;
+ const char *bol, *eol;
+ size_t len;
+
+ bol = find_commit_header(msg, key, &len);
+ if (!bol)
+ return NULL;
+ eol = bol + len;
+ strbuf_add(&val, bol, len);
+
+ while (eol[0] == '\n' && eol[1] == ' ') {
+ bol = eol + 2;
+ eol = strchrnul(bol, '\n');
+ strbuf_addch(&val, '\n');
+ strbuf_add(&val, bol, eol - bol);
+ }
+
+ *end = eol;
+ return strbuf_detach(&val, NULL);
+}
+
static void handle_commit(struct commit *commit, struct rev_info *rev,
struct string_list *paths_of_changed_objects)
{
int saved_output_format = rev->diffopt.output_format;
- const char *commit_buffer;
+ const char *commit_buffer, *commit_buffer_cursor;
const char *author, *author_end, *committer, *committer_end;
- const char *encoding, *message;
+ const char *encoding = NULL;
+ size_t encoding_len;
+ const char *signature_alg = NULL, *signature = NULL;
+ const char *message;
char *reencoded = NULL;
struct commit_list *p;
const char *refname;
@@ -641,21 +670,43 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
rev->diffopt.output_format = DIFF_FORMAT_CALLBACK;
parse_commit_or_die(commit);
- commit_buffer = repo_get_commit_buffer(the_repository, commit, NULL);
- author = strstr(commit_buffer, "\nauthor ");
+ commit_buffer_cursor = commit_buffer = repo_get_commit_buffer(the_repository, commit, NULL);
+
+ author = strstr(commit_buffer_cursor, "\nauthor ");
if (!author)
die("could not find author in commit %s",
oid_to_hex(&commit->object.oid));
author++;
- author_end = strchrnul(author, '\n');
- committer = strstr(author_end, "\ncommitter ");
+ commit_buffer_cursor = author_end = strchrnul(author, '\n');
+
+ committer = strstr(commit_buffer_cursor, "\ncommitter ");
if (!committer)
die("could not find committer in commit %s",
oid_to_hex(&commit->object.oid));
committer++;
- committer_end = strchrnul(committer, '\n');
- message = strstr(committer_end, "\n\n");
- encoding = find_encoding(committer_end, message);
+ commit_buffer_cursor = committer_end = strchrnul(committer, '\n');
+
+ /*
+ * find_commit_header() and find_commit_multiline_header() get
+ * a `+ 1` because commit_buffer_cursor points at the trailing
+ * "\n" at the end of the previous line, but they want a
+ * pointer to the beginning of the next line.
+ */
+
+ if (*commit_buffer_cursor == '\n') {
+ encoding = find_commit_header(commit_buffer_cursor + 1, "encoding", &encoding_len);
+ if (encoding)
+ commit_buffer_cursor = encoding + encoding_len;
+ }
+
+ if (*commit_buffer_cursor == '\n') {
+ if ((signature = find_commit_multiline_header(commit_buffer_cursor + 1, "gpgsig", &commit_buffer_cursor)))
+ signature_alg = "sha1";
+ else if ((signature = find_commit_multiline_header(commit_buffer_cursor + 1, "gpgsig-sha256", &commit_buffer_cursor)))
+ signature_alg = "sha256";
+ }
+
+ message = strstr(commit_buffer_cursor, "\n\n");
if (message)
message += 2;
@@ -694,16 +745,20 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
if (anonymize) {
reencoded = anonymize_commit_message();
} else if (encoding) {
- switch(reencode_mode) {
+ char *buf;
+ switch (reencode_mode) {
case REENCODE_YES:
- reencoded = reencode_string(message, "UTF-8", encoding);
+ buf = xstrfmt("%.*s", (int)encoding_len, encoding);
+ reencoded = reencode_string(message, "UTF-8", buf);
+ free(buf);
break;
case REENCODE_NO:
break;
case REENCODE_ABORT:
- die("Encountered commit-specific encoding %s in commit "
+ die("Encountered commit-specific encoding %.*s in commit "
"%s; use --reencode=[yes|no] to handle it",
- encoding, oid_to_hex(&commit->object.oid));
+ (int)encoding_len, encoding,
+ oid_to_hex(&commit->object.oid));
}
}
if (!commit->parents)
@@ -714,8 +769,33 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
printf("%.*s\n%.*s\n",
(int)(author_end - author), author,
(int)(committer_end - committer), committer);
+ if (signature) {
+ switch (signed_commit_mode) {
+ case SIGN_ABORT:
+ die("encountered signed commit %s; use "
+ "--signed-commits=<mode> to handle it",
+ oid_to_hex(&commit->object.oid));
+ case SIGN_WARN_VERBATIM:
+ warning("exporting signed commit %s",
+ oid_to_hex(&commit->object.oid));
+ /* fallthru */
+ case SIGN_VERBATIM:
+ printf("gpgsig %s\ndata %u\n%s",
+ signature_alg,
+ (unsigned)strlen(signature),
+ signature);
+ break;
+ case SIGN_WARN_STRIP:
+ warning("stripping signature from commit %s",
+ oid_to_hex(&commit->object.oid));
+ /* fallthru */
+ case SIGN_STRIP:
+ break;
+ }
+ free((char *)signature);
+ }
if (!reencoded && encoding)
- printf("encoding %s\n", encoding);
+ printf("encoding %.*s\n", (int)encoding_len, encoding);
printf("data %u\n%s",
(unsigned)(reencoded
? strlen(reencoded) : message
@@ -828,22 +908,22 @@ static void handle_tag(const char *name, struct tag *tag)
const char *signature = strstr(message,
"\n-----BEGIN PGP SIGNATURE-----\n");
if (signature)
- switch(signed_tag_mode) {
- case SIGNED_TAG_ABORT:
+ switch (signed_tag_mode) {
+ case SIGN_ABORT:
die("encountered signed tag %s; use "
"--signed-tags=<mode> to handle it",
oid_to_hex(&tag->object.oid));
- case WARN:
+ case SIGN_WARN_VERBATIM:
warning("exporting signed tag %s",
oid_to_hex(&tag->object.oid));
/* fallthru */
- case VERBATIM:
+ case SIGN_VERBATIM:
break;
- case WARN_STRIP:
+ case SIGN_WARN_STRIP:
warning("stripping signature from tag %s",
oid_to_hex(&tag->object.oid));
/* fallthru */
- case STRIP:
+ case SIGN_STRIP:
message_size = signature + 1 - message;
break;
}
@@ -853,7 +933,7 @@ static void handle_tag(const char *name, struct tag *tag)
tagged = tag->tagged;
tagged_mark = get_object_mark(tagged);
if (!tagged_mark) {
- switch(tag_of_filtered_mode) {
+ switch (tag_of_filtered_mode) {
case TAG_FILTERING_ABORT:
die("tag %s tags unexported object; use "
"--tag-of-filtered-object=<mode> to handle it",
@@ -965,7 +1045,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
continue;
}
- switch(commit->object.type) {
+ switch (commit->object.type) {
case OBJ_COMMIT:
break;
case OBJ_BLOB:
@@ -1189,6 +1269,7 @@ int cmd_fast_export(int argc,
const char *prefix,
struct repository *repo UNUSED)
{
+ const char *env_signed_commits_noabort;
struct rev_info revs;
struct commit *commit;
char *export_filename = NULL,
@@ -1202,7 +1283,10 @@ int cmd_fast_export(int argc,
N_("show progress after <n> objects")),
OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, N_("mode"),
N_("select handling of signed tags"),
- parse_opt_signed_tag_mode),
+ parse_opt_sign_mode),
+ OPT_CALLBACK(0, "signed-commits", &signed_commit_mode, N_("mode"),
+ N_("select handling of signed commits"),
+ parse_opt_sign_mode),
OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, N_("mode"),
N_("select handling of tags that tag filtered objects"),
parse_opt_tag_of_filtered_mode),
@@ -1243,6 +1327,10 @@ int cmd_fast_export(int argc,
if (argc == 1)
usage_with_options (fast_export_usage, options);
+ env_signed_commits_noabort = getenv("FAST_EXPORT_SIGNED_COMMITS_NOABORT");
+ if (env_signed_commits_noabort && *env_signed_commits_noabort)
+ signed_commit_mode = SIGN_WARN_STRIP;
+
/* we handle encodings */
git_config(git_default_config, NULL);
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index d6a368a566..e432e8d5a1 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -328,7 +328,7 @@ static void write_branch_report(FILE *rpt, struct branch *b)
static void write_crash_report(const char *err)
{
- char *loc = git_pathdup("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
+ char *loc = repo_git_path(the_repository, "fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
FILE *rpt = fopen(loc, "w");
struct branch *b;
unsigned long lu;
@@ -2719,10 +2719,13 @@ static struct hash_list *parse_merge(unsigned int *count)
static void parse_new_commit(const char *arg)
{
+ static struct strbuf sig = STRBUF_INIT;
static struct strbuf msg = STRBUF_INIT;
+ struct string_list siglines = STRING_LIST_INIT_NODUP;
struct branch *b;
char *author = NULL;
char *committer = NULL;
+ char *sig_alg = NULL;
char *encoding = NULL;
struct hash_list *merge_list = NULL;
unsigned int merge_count;
@@ -2746,6 +2749,13 @@ static void parse_new_commit(const char *arg)
}
if (!committer)
die("Expected committer but didn't get one");
+ if (skip_prefix(command_buf.buf, "gpgsig ", &v)) {
+ sig_alg = xstrdup(v);
+ read_next_command();
+ parse_data(&sig, 0, NULL);
+ read_next_command();
+ } else
+ strbuf_setlen(&sig, 0);
if (skip_prefix(command_buf.buf, "encoding ", &v)) {
encoding = xstrdup(v);
read_next_command();
@@ -2819,10 +2829,23 @@ static void parse_new_commit(const char *arg)
strbuf_addf(&new_data,
"encoding %s\n",
encoding);
+ if (sig_alg) {
+ if (!strcmp(sig_alg, "sha1"))
+ strbuf_addstr(&new_data, "gpgsig ");
+ else if (!strcmp(sig_alg, "sha256"))
+ strbuf_addstr(&new_data, "gpgsig-sha256 ");
+ else
+ die("Expected gpgsig algorithm sha1 or sha256, got %s", sig_alg);
+ string_list_split_in_place(&siglines, sig.buf, "\n", -1);
+ strbuf_add_separated_string_list(&new_data, "\n ", &siglines);
+ strbuf_addch(&new_data, '\n');
+ }
strbuf_addch(&new_data, '\n');
strbuf_addbuf(&new_data, &msg);
+ string_list_clear(&siglines, 1);
free(author);
free(committer);
+ free(sig_alg);
free(encoding);
if (!store_object(OBJ_COMMIT, &new_data, NULL, &b->oid, next_mark))
@@ -3280,7 +3303,7 @@ static char* make_fast_import_path(const char *path)
{
if (!relative_marks_paths || is_absolute_path(path))
return prefix_filename(global_prefix, path);
- return git_pathdup("info/fast-import/%s", path);
+ return repo_git_path(the_repository, "info/fast-import/%s", path);
}
static void option_import_marks(const char *marks,
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 1c740d5aac..9830c09011 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -586,7 +586,7 @@ static struct ref *get_ref_map(struct remote *remote,
struct refspec_item tag_refspec;
/* also fetch all tags */
- refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
+ refspec_item_init_push(&tag_refspec, TAG_REFSPEC);
get_fetch_map(remote_refs, &tag_refspec, &tail, 0);
refspec_item_clear(&tag_refspec);
} else if (tags == TAGS_DEFAULT && *autotags) {
@@ -1718,7 +1718,6 @@ static int do_fetch(struct transport *transport,
const struct ref *remote_refs;
struct transport_ls_refs_options transport_ls_refs_options =
TRANSPORT_LS_REFS_OPTIONS_INIT;
- int must_list_refs = 1;
struct fetch_head fetch_head = { 0 };
struct strbuf err = STRBUF_INIT;
@@ -1737,21 +1736,7 @@ static int do_fetch(struct transport *transport,
}
if (rs->nr) {
- int i;
-
refspec_ref_prefixes(rs, &transport_ls_refs_options.ref_prefixes);
-
- /*
- * We can avoid listing refs if all of them are exact
- * OIDs
- */
- must_list_refs = 0;
- for (i = 0; i < rs->nr; i++) {
- if (!rs->items[i].exact_sha1) {
- must_list_refs = 1;
- break;
- }
- }
} else {
struct branch *branch = branch_get(NULL);
@@ -1766,21 +1751,30 @@ static int do_fetch(struct transport *transport,
branch->merge[i]->src);
}
}
- }
- if (tags == TAGS_SET || tags == TAGS_DEFAULT) {
- must_list_refs = 1;
- if (transport_ls_refs_options.ref_prefixes.nr)
+ /*
+ * If there are no refs specified to fetch, then we just
+ * fetch HEAD; mention that to narrow the advertisement.
+ */
+ if (!transport_ls_refs_options.ref_prefixes.nr)
strvec_push(&transport_ls_refs_options.ref_prefixes,
- "refs/tags/");
+ "HEAD");
}
- if (uses_remote_tracking(transport, rs)) {
- must_list_refs = 1;
- strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
- }
+ if (tags == TAGS_SET || tags == TAGS_DEFAULT)
+ strvec_push(&transport_ls_refs_options.ref_prefixes,
+ "refs/tags/");
- if (must_list_refs) {
+ if (transport_ls_refs_options.ref_prefixes.nr &&
+ uses_remote_tracking(transport, rs))
+ strvec_push(&transport_ls_refs_options.ref_prefixes,
+ "HEAD");
+
+ /*
+ * Only initiate ref listing if we have at least one ref we want to
+ * know about.
+ */
+ if (transport_ls_refs_options.ref_prefixes.nr) {
trace2_region_enter("fetch", "remote_refs", the_repository);
remote_refs = transport_get_remote_refs(transport,
&transport_ls_refs_options);
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 8085ebd8fe..3d2207ec77 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
#include "builtin.h"
#include "commit.h"
#include "config.h"
@@ -20,7 +19,7 @@ static char const * const for_each_ref_usage[] = {
int cmd_for_each_ref(int argc,
const char **argv,
const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
struct ref_sorting *sorting;
struct string_list sorting_options = STRING_LIST_INIT_DUP;
@@ -63,7 +62,7 @@ int cmd_for_each_ref(int argc,
format.format = "%(objectname) %(objecttype)\t%(refname)";
- git_config(git_default_config, NULL);
+ repo_config(repo, git_default_config, NULL);
/* Set default (refname) sorting */
string_list_append(&sorting_options, "refname");
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 7a4dcb0716..8fbd1d6334 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -50,6 +50,7 @@ static int verbose;
static int show_progress = -1;
static int show_dangling = 1;
static int name_objects;
+static int check_references = 1;
#define ERROR_OBJECT 01
#define ERROR_REACHABLE 02
#define ERROR_PACK 04
@@ -326,7 +327,7 @@ static void check_unreachable_object(struct object *obj)
printable_type(&obj->oid, obj->type),
describe_object(&obj->oid));
if (write_lost_and_found) {
- char *filename = git_pathdup("lost-found/%s/%s",
+ char *filename = repo_git_path(the_repository, "lost-found/%s/%s",
obj->type == OBJ_COMMIT ? "commit" : "other",
describe_object(&obj->oid));
FILE *f;
@@ -905,11 +906,37 @@ static int check_pack_rev_indexes(struct repository *r, int show_progress)
return res;
}
+static void fsck_refs(struct repository *r)
+{
+ struct child_process refs_verify = CHILD_PROCESS_INIT;
+ struct progress *progress = NULL;
+
+ if (show_progress)
+ progress = start_progress(r, _("Checking ref database"), 1);
+
+ if (verbose)
+ fprintf_ln(stderr, _("Checking ref database"));
+
+ child_process_init(&refs_verify);
+ refs_verify.git_cmd = 1;
+ strvec_pushl(&refs_verify.args, "refs", "verify", NULL);
+ if (verbose)
+ strvec_push(&refs_verify.args, "--verbose");
+ if (check_strict)
+ strvec_push(&refs_verify.args, "--strict");
+
+ if (run_command(&refs_verify))
+ errors_found |= ERROR_REFS;
+
+ display_progress(progress, 1);
+ stop_progress(&progress);
+}
+
static char const * const fsck_usage[] = {
N_("git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n"
" [--[no-]full] [--strict] [--verbose] [--lost-found]\n"
" [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n"
- " [--[no-]name-objects] [<object>...]"),
+ " [--[no-]name-objects] [--[no-]references] [<object>...]"),
NULL
};
@@ -928,6 +955,7 @@ static struct option fsck_opts[] = {
N_("write dangling objects in .git/lost-found")),
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
OPT_BOOL(0, "name-objects", &name_objects, N_("show verbose names for reachable objects")),
+ OPT_BOOL(0, "references", &check_references, N_("check reference database consistency")),
OPT_END(),
};
@@ -970,6 +998,9 @@ int cmd_fsck(int argc,
git_config(git_fsck_config, &fsck_obj_options);
prepare_repo_settings(the_repository);
+ if (check_references)
+ fsck_refs(the_repository);
+
if (connectivity_only) {
for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
for_each_packed_object(the_repository,
@@ -1057,7 +1088,7 @@ int cmd_fsck(int argc,
struct worktree *wt = *p;
struct index_state istate =
INDEX_STATE_INIT(the_repository);
- char *path;
+ char *path, *wt_gitdir;
/*
* Make a copy since the buffer is reusable
@@ -1065,9 +1096,13 @@ int cmd_fsck(int argc,
* while we're examining the index.
*/
path = xstrdup(worktree_git_path(the_repository, wt, "index"));
- read_index_from(&istate, path, get_worktree_git_dir(wt));
+ wt_gitdir = get_worktree_git_dir(wt);
+
+ read_index_from(&istate, path, wt_gitdir);
fsck_index(&istate, path, wt->is_current);
+
discard_index(&istate);
+ free(wt_gitdir);
free(path);
}
free_worktrees(worktrees);
diff --git a/builtin/gc.c b/builtin/gc.c
index 409d454a4b..99431fd467 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -99,9 +99,11 @@ static void process_log_file(void)
/* There was some error recorded in the lock file */
commit_lock_file(&log_lock);
} else {
+ char *path = repo_git_path(the_repository, "gc.log");
/* No error, clean up any old gc.log */
- unlink(git_path("gc.log"));
+ unlink(path);
rollback_lock_file(&log_lock);
+ free(path);
}
}
@@ -300,8 +302,11 @@ static int too_many_loose_objects(struct gc_config *cfg)
int num_loose = 0;
int needed = 0;
const unsigned hexsz_loose = the_hash_algo->hexsz - 2;
+ char *path;
- dir = opendir(git_path("objects/17"));
+ path = repo_git_path(the_repository, "objects/17");
+ dir = opendir(path);
+ free(path);
if (!dir)
return 0;
@@ -550,7 +555,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
if (xgethostname(my_host, sizeof(my_host)))
xsnprintf(my_host, sizeof(my_host), "unknown");
- pidfile_path = git_pathdup("gc.pid");
+ pidfile_path = repo_git_path(the_repository, "gc.pid");
fd = hold_lock_file_for_update(&lock, pidfile_path,
LOCK_DIE_ON_ERROR);
if (!force) {
@@ -611,7 +616,7 @@ static int report_last_gc_error(void)
int ret = 0;
ssize_t len;
struct stat st;
- char *gc_log_path = git_pathdup("gc.log");
+ char *gc_log_path = repo_git_path(the_repository, "gc.log");
if (stat(gc_log_path, &st)) {
if (errno == ENOENT)
@@ -826,11 +831,12 @@ struct repository *repo UNUSED)
}
if (daemonized) {
- hold_lock_file_for_update(&log_lock,
- git_path("gc.log"),
+ char *path = repo_git_path(the_repository, "gc.log");
+ hold_lock_file_for_update(&log_lock, path,
LOCK_DIE_ON_ERROR);
dup2(get_lock_file_fd(&log_lock), 2);
atexit(process_log_file_at_exit);
+ free(path);
}
gc_before_repack(&opts, &cfg);
@@ -892,8 +898,11 @@ struct repository *repo UNUSED)
warning(_("There are too many unreachable loose objects; "
"run 'git prune' to remove them."));
- if (!daemonized)
- unlink(git_path("gc.log"));
+ if (!daemonized) {
+ char *path = repo_git_path(the_repository, "gc.log");
+ unlink(path);
+ free(path);
+ }
out:
gc_config_release(&cfg);
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 52cc97d52c..50573ba049 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1286,6 +1286,7 @@ static void parse_pack_objects(unsigned char *hash)
/* Check pack integrity */
flush();
+ the_hash_algo->init_fn(&tmp_ctx);
git_hash_clone(&tmp_ctx, &input_ctx);
git_hash_final(hash, &tmp_ctx);
if (!hasheq(fill(the_hash_algo->rawsz), hash, the_repository->hash_algo))
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 096f96b9c4..196dccdd77 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -132,8 +132,8 @@ int cmd_init_db(int argc,
* and we know shared_repository should always be 0;
* but just in case we play safe.
*/
- saved = get_shared_repository();
- set_shared_repository(0);
+ saved = repo_settings_get_shared_repository(the_repository);
+ repo_settings_set_shared_repository(the_repository, 0);
switch (safe_create_leading_directories_const(argv[0])) {
case SCLD_OK:
case SCLD_PERMS:
@@ -145,7 +145,7 @@ int cmd_init_db(int argc,
die_errno(_("cannot mkdir %s"), argv[0]);
break;
}
- set_shared_repository(saved);
+ repo_settings_set_shared_repository(the_repository, saved);
if (mkdir(argv[0], 0777) < 0)
die_errno(_("cannot mkdir %s"), argv[0]);
mkdir_tried = 1;
@@ -175,7 +175,7 @@ int cmd_init_db(int argc,
}
if (init_shared_repository != -1)
- set_shared_repository(init_shared_repository);
+ repo_settings_set_shared_repository(the_repository, init_shared_repository);
/*
* GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
diff --git a/builtin/log.c b/builtin/log.c
index e41f88945e..04a6ef97bc 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -2309,8 +2309,8 @@ int cmd_format_patch(int argc,
* We consider <outdir> as 'outside of gitdir', therefore avoid
* applying adjust_shared_perm in s-c-l-d.
*/
- saved = get_shared_repository();
- set_shared_repository(0);
+ saved = repo_settings_get_shared_repository(the_repository);
+ repo_settings_set_shared_repository(the_repository, 0);
switch (safe_create_leading_directories_const(output_directory)) {
case SCLD_OK:
case SCLD_EXISTS:
@@ -2319,7 +2319,7 @@ int cmd_format_patch(int argc,
die(_("could not create leading directories "
"of '%s'"), output_directory);
}
- set_shared_repository(saved);
+ repo_settings_set_shared_repository(the_repository, saved);
if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
die_errno(_("could not create directory '%s'"),
output_directory);
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index a4431429b7..70a377e9c0 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -6,7 +6,6 @@
* Copyright (C) Linus Torvalds, 2005
*/
-#define USE_THE_REPOSITORY_VARIABLE
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "builtin.h"
@@ -245,12 +244,13 @@ static void show_submodule(struct repository *superproject,
repo_clear(&subrepo);
}
-static void expand_objectsize(struct strbuf *line, const struct object_id *oid,
+static void expand_objectsize(struct repository *repo, struct strbuf *line,
+ const struct object_id *oid,
const enum object_type type, unsigned int padded)
{
if (type == OBJ_BLOB) {
unsigned long size;
- if (oid_object_info(the_repository, oid, &size) < 0)
+ if (oid_object_info(repo, oid, &size) < 0)
die(_("could not get object info about '%s'"),
oid_to_hex(oid));
if (padded)
@@ -283,10 +283,10 @@ static void show_ce_fmt(struct repository *repo, const struct cache_entry *ce,
else if (skip_prefix(format, "(objecttype)", &format))
strbuf_addstr(&sb, type_name(object_type(ce->ce_mode)));
else if (skip_prefix(format, "(objectsize:padded)", &format))
- expand_objectsize(&sb, &ce->oid,
+ expand_objectsize(repo, &sb, &ce->oid,
object_type(ce->ce_mode), 1);
else if (skip_prefix(format, "(objectsize)", &format))
- expand_objectsize(&sb, &ce->oid,
+ expand_objectsize(repo, &sb, &ce->oid,
object_type(ce->ce_mode), 0);
else if (skip_prefix(format, "(stage)", &format))
strbuf_addf(&sb, "%d", ce_stage(ce));
@@ -348,7 +348,7 @@ static void show_ce(struct repository *repo, struct dir_struct *dir,
}
}
-static void show_ru_info(struct index_state *istate)
+static void show_ru_info(struct repository *repo, struct index_state *istate)
{
struct string_list_item *item;
@@ -370,7 +370,7 @@ static void show_ru_info(struct index_state *istate)
if (!ui->mode[i])
continue;
printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
- repo_find_unique_abbrev(the_repository, &ui->oid[i], abbrev),
+ repo_find_unique_abbrev(repo, &ui->oid[i], abbrev),
i + 1);
write_name(path);
}
@@ -567,7 +567,7 @@ static int option_parse_exclude_standard(const struct option *opt,
int cmd_ls_files(int argc,
const char **argv,
const char *cmd_prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
int require_work_tree = 0, show_tag = 0, i;
char *max_prefix;
@@ -647,15 +647,15 @@ int cmd_ls_files(int argc,
show_usage_with_options_if_asked(argc, argv,
ls_files_usage, builtin_ls_files_options);
- prepare_repo_settings(the_repository);
- the_repository->settings.command_requires_full_index = 0;
+ prepare_repo_settings(repo);
+ repo->settings.command_requires_full_index = 0;
prefix = cmd_prefix;
if (prefix)
prefix_len = strlen(prefix);
- git_config(git_default_config, NULL);
+ repo_config(repo, git_default_config, NULL);
- if (repo_read_index(the_repository) < 0)
+ if (repo_read_index(repo) < 0)
die("index file corrupt");
argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
@@ -724,7 +724,7 @@ int cmd_ls_files(int argc,
max_prefix = common_prefix(&pathspec);
max_prefix_len = get_common_prefix_len(max_prefix);
- prune_index(the_repository->index, max_prefix, max_prefix_len);
+ prune_index(repo->index, max_prefix, max_prefix_len);
/* Treat unmatching pathspec elements as errors */
if (pathspec.nr && error_unmatch)
@@ -748,13 +748,13 @@ int cmd_ls_files(int argc,
*/
if (show_stage || show_unmerged)
die(_("options '%s' and '%s' cannot be used together"), "ls-files --with-tree", "-s/-u");
- overlay_tree_on_index(the_repository->index, with_tree, max_prefix);
+ overlay_tree_on_index(repo->index, with_tree, max_prefix);
}
- show_files(the_repository, &dir);
+ show_files(repo, &dir);
if (show_resolve_undo)
- show_ru_info(the_repository->index);
+ show_ru_info(repo, repo->index);
if (ps_matched && report_path_error(ps_matched, &pathspec)) {
fprintf(stderr, "Did you forget to 'git add'?\n");
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index beac166b5c..65f867d7a4 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -567,7 +567,11 @@ int cmd_name_rev(int argc,
{
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;
+
+#ifndef WITH_BREAKING_CHANGES
+ int transform_stdin = 0;
+#endif
+ int all = 0, annotate_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
struct option opts[] = {
OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")),
@@ -578,11 +582,13 @@ int cmd_name_rev(int argc,
N_("ignore refs matching <pattern>")),
OPT_GROUP(""),
OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
+#ifndef WITH_BREAKING_CHANGES
OPT_BOOL_F(0,
"stdin",
&transform_stdin,
N_("deprecated: use --annotate-stdin instead"),
PARSE_OPT_HIDDEN),
+#endif /* WITH_BREAKING_CHANGES */
OPT_BOOL(0, "annotate-stdin", &annotate_stdin, N_("annotate text from stdin")),
OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")),
OPT_BOOL(0, "always", &always,
@@ -597,12 +603,14 @@ int cmd_name_rev(int argc,
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0);
+#ifndef WITH_BREAKING_CHANGES
if (transform_stdin) {
warning("--stdin is deprecated. Please use --annotate-stdin instead, "
"which is functionally equivalent.\n"
"This option will be removed in a future release.");
annotate_stdin = 1;
}
+#endif
if (all + annotate_stdin + !!argc > 1) {
error("Specify either a list, or --all, not both!");
diff --git a/builtin/notes.c b/builtin/notes.c
index d051abf6df..ff61ec5f2d 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -197,7 +197,7 @@ static void prepare_note_data(const struct object_id *object, struct note_data *
struct strbuf buf = STRBUF_INIT;
/* write the template message before editing: */
- d->edit_path = git_pathdup("NOTES_EDITMSG");
+ d->edit_path = repo_git_path(the_repository, "NOTES_EDITMSG");
fd = xopen(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (d->msg_nr)
@@ -979,6 +979,8 @@ static int merge(int argc, const char **argv, const char *prefix,
else { /* Merge has unresolved conflicts */
struct worktree **worktrees;
const struct worktree *wt;
+ char *path;
+
/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
refs_update_ref(get_main_ref_store(the_repository), msg.buf,
"NOTES_MERGE_PARTIAL", &result_oid, NULL,
@@ -994,10 +996,13 @@ static int merge(int argc, const char **argv, const char *prefix,
if (refs_update_symref(get_main_ref_store(the_repository), "NOTES_MERGE_REF", notes_ref, NULL))
die(_("failed to store link to current notes ref (%s)"),
notes_ref);
+
+ path = repo_git_path(the_repository, NOTES_MERGE_WORKTREE);
fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s "
"and commit the result with 'git notes merge --commit', "
"or abort the merge with 'git notes merge --abort'.\n"),
- git_path(NOTES_MERGE_WORKTREE));
+ path);
+ free(path);
}
free_notes(t);
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 58a9b16126..6b06d159d2 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -206,6 +206,7 @@ static int have_non_local_packs;
static int incremental;
static int ignore_packed_keep_on_disk;
static int ignore_packed_keep_in_core;
+static int ignore_packed_keep_in_core_has_cruft;
static int allow_ofs_delta;
static struct pack_idx_option pack_idx_opts;
static const char *base_name;
@@ -1397,7 +1398,8 @@ static void write_pack_file(void)
if (write_bitmap_index) {
bitmap_writer_init(&bitmap_writer,
- the_repository, &to_pack);
+ the_repository, &to_pack,
+ NULL);
bitmap_writer_set_checksum(&bitmap_writer, hash);
bitmap_writer_build_type_index(&bitmap_writer,
written_list);
@@ -1502,8 +1504,60 @@ static int have_duplicate_entry(const struct object_id *oid,
return 1;
}
+static int want_cruft_object_mtime(struct repository *r,
+ const struct object_id *oid,
+ unsigned flags, uint32_t mtime)
+{
+ struct packed_git **cache;
+
+ for (cache = kept_pack_cache(r, flags); *cache; cache++) {
+ struct packed_git *p = *cache;
+ off_t ofs;
+ uint32_t candidate_mtime;
+
+ ofs = find_pack_entry_one(oid, p);
+ if (!ofs)
+ continue;
+
+ /*
+ * We have a copy of the object 'oid' in a non-cruft
+ * pack. We can avoid packing an additional copy
+ * regardless of what the existing copy's mtime is since
+ * it is outside of a cruft pack.
+ */
+ if (!p->is_cruft)
+ return 0;
+
+ /*
+ * If we have a copy of the object 'oid' in a cruft
+ * pack, then either read the cruft pack's mtime for
+ * that object, or, if that can't be loaded, assume the
+ * pack's mtime itself.
+ */
+ if (!load_pack_mtimes(p)) {
+ uint32_t pos;
+ if (offset_to_pack_pos(p, ofs, &pos) < 0)
+ continue;
+ candidate_mtime = nth_packed_mtime(p, pos);
+ } else {
+ candidate_mtime = p->mtime;
+ }
+
+ /*
+ * We have a surviving copy of the object in a cruft
+ * pack whose mtime is greater than or equal to the one
+ * we are considering. We can thus avoid packing an
+ * additional copy of that object.
+ */
+ if (mtime <= candidate_mtime)
+ return 0;
+ }
+
+ return -1;
+}
+
static int want_found_object(const struct object_id *oid, int exclude,
- struct packed_git *p)
+ struct packed_git *p, uint32_t mtime)
{
if (exclude)
return 1;
@@ -1553,12 +1607,29 @@ static int want_found_object(const struct object_id *oid, int exclude,
if (ignore_packed_keep_in_core)
flags |= IN_CORE_KEEP_PACKS;
- if (ignore_packed_keep_on_disk && p->pack_keep)
- return 0;
- if (ignore_packed_keep_in_core && p->pack_keep_in_core)
- return 0;
- if (has_object_kept_pack(p->repo, oid, flags))
- return 0;
+ /*
+ * If the object is in a pack that we want to ignore, *and* we
+ * don't have any cruft packs that are being retained, we can
+ * abort quickly.
+ */
+ if (!ignore_packed_keep_in_core_has_cruft) {
+ if (ignore_packed_keep_on_disk && p->pack_keep)
+ return 0;
+ if (ignore_packed_keep_in_core && p->pack_keep_in_core)
+ return 0;
+ if (has_object_kept_pack(p->repo, oid, flags))
+ return 0;
+ } else {
+ /*
+ * But if there is at least one cruft pack which
+ * is being kept, we only want to include the
+ * provided object if it has a strictly greater
+ * mtime than any existing cruft copy.
+ */
+ if (!want_cruft_object_mtime(p->repo, oid, flags,
+ mtime))
+ return 0;
+ }
}
/*
@@ -1577,7 +1648,8 @@ static int want_object_in_pack_one(struct packed_git *p,
const struct object_id *oid,
int exclude,
struct packed_git **found_pack,
- off_t *found_offset)
+ off_t *found_offset,
+ uint32_t found_mtime)
{
off_t offset;
@@ -1593,7 +1665,7 @@ static int want_object_in_pack_one(struct packed_git *p,
*found_offset = offset;
*found_pack = p;
}
- return want_found_object(oid, exclude, p);
+ return want_found_object(oid, exclude, p, found_mtime);
}
return -1;
}
@@ -1607,10 +1679,11 @@ static int want_object_in_pack_one(struct packed_git *p,
* function finds if there is any pack that has the object and returns the pack
* and its offset in these variables.
*/
-static int want_object_in_pack(const struct object_id *oid,
- int exclude,
- struct packed_git **found_pack,
- off_t *found_offset)
+static int want_object_in_pack_mtime(const struct object_id *oid,
+ int exclude,
+ struct packed_git **found_pack,
+ off_t *found_offset,
+ uint32_t found_mtime)
{
int want;
struct list_head *pos;
@@ -1625,7 +1698,8 @@ static int want_object_in_pack(const struct object_id *oid,
* are present we will determine the answer right now.
*/
if (*found_pack) {
- want = want_found_object(oid, exclude, *found_pack);
+ want = want_found_object(oid, exclude, *found_pack,
+ found_mtime);
if (want != -1)
return want;
@@ -1636,7 +1710,7 @@ static int want_object_in_pack(const struct object_id *oid,
for (m = get_multi_pack_index(the_repository); m; m = m->next) {
struct pack_entry e;
if (fill_midx_entry(the_repository, oid, &e, m)) {
- want = want_object_in_pack_one(e.p, oid, exclude, found_pack, found_offset);
+ want = want_object_in_pack_one(e.p, oid, exclude, found_pack, found_offset, found_mtime);
if (want != -1)
return want;
}
@@ -1644,7 +1718,7 @@ static int want_object_in_pack(const struct object_id *oid,
list_for_each(pos, get_packed_git_mru(the_repository)) {
struct packed_git *p = list_entry(pos, struct packed_git, mru);
- want = want_object_in_pack_one(p, oid, exclude, found_pack, found_offset);
+ want = want_object_in_pack_one(p, oid, exclude, found_pack, found_offset, found_mtime);
if (!exclude && want > 0)
list_move(&p->mru,
get_packed_git_mru(the_repository));
@@ -1674,6 +1748,15 @@ static int want_object_in_pack(const struct object_id *oid,
return 1;
}
+static inline int want_object_in_pack(const struct object_id *oid,
+ int exclude,
+ struct packed_git **found_pack,
+ off_t *found_offset)
+{
+ return want_object_in_pack_mtime(oid, exclude, found_pack, found_offset,
+ 0);
+}
+
static struct object_entry *create_object_entry(const struct object_id *oid,
enum object_type type,
uint32_t hash,
@@ -3606,7 +3689,7 @@ static void add_cruft_object_entry(const struct object_id *oid, enum object_type
entry->no_try_delta = no_try_delta(name);
}
} else {
- if (!want_object_in_pack(oid, 0, &pack, &offset))
+ if (!want_object_in_pack_mtime(oid, 0, &pack, &offset, mtime))
return;
if (!pack && type == OBJ_BLOB && !has_loose_object(oid)) {
/*
@@ -3680,6 +3763,8 @@ static void mark_pack_kept_in_core(struct string_list *packs, unsigned keep)
struct packed_git *p = item->util;
if (!p)
die(_("could not find pack '%s'"), item->string);
+ if (p->is_cruft && keep)
+ ignore_packed_keep_in_core_has_cruft = 1;
p->pack_keep_in_core = keep;
}
}
diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c
index 4fdd68880e..e47bae1c80 100644
--- a/builtin/pack-refs.c
+++ b/builtin/pack-refs.c
@@ -1,5 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
-
#include "builtin.h"
#include "config.h"
#include "gettext.h"
@@ -15,7 +13,7 @@ static char const * const pack_refs_usage[] = {
int cmd_pack_refs(int argc,
const char **argv,
const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
struct ref_exclusions excludes = REF_EXCLUSIONS_INIT;
struct string_list included_refs = STRING_LIST_INIT_NODUP;
@@ -39,7 +37,7 @@ int cmd_pack_refs(int argc,
N_("references to exclude")),
OPT_END(),
};
- git_config(git_default_config, NULL);
+ repo_config(repo, git_default_config, NULL);
if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
usage_with_options(pack_refs_usage, opts);
@@ -52,7 +50,7 @@ int cmd_pack_refs(int argc,
if (!pack_refs_opts.includes->nr)
string_list_append(pack_refs_opts.includes, "refs/tags/*");
- ret = refs_pack_refs(get_main_ref_store(the_repository), &pack_refs_opts);
+ ret = refs_pack_refs(get_main_ref_store(repo), &pack_refs_opts);
clear_ref_exclusions(&excludes);
string_list_clear(&included_refs, 0);
diff --git a/builtin/pull.c b/builtin/pull.c
index 9c4a00620a..a1ebc6ad33 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -738,7 +738,8 @@ static const char *get_tracking_branch(const char *remote, const char *refspec)
const char *spec_src;
const char *merge_branch;
- refspec_item_init_or_die(&spec, refspec, REFSPEC_FETCH);
+ if (!refspec_item_init_fetch(&spec, refspec))
+ die(_("invalid refspec '%s'"), refspec);
spec_src = spec.src;
if (!*spec_src || !strcmp(spec_src, "HEAD"))
spec_src = "HEAD";
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 6c9eaf3788..d4715ed35d 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -644,7 +644,7 @@ static int run_am(struct rebase_options *opts)
return run_command(&am);
}
- rebased_patches = xstrdup(git_path("rebased-patches"));
+ rebased_patches = repo_git_path(the_repository, "rebased-patches");
format_patch.out = open(rebased_patches,
O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (format_patch.out < 0) {
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 129305700c..7b28fc9df6 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1435,7 +1435,8 @@ static const char *push_to_checkout(unsigned char *hash,
static const char *update_worktree(unsigned char *sha1, const struct worktree *worktree)
{
- const char *retval, *git_dir;
+ const char *retval;
+ char *git_dir;
struct strvec env = STRVEC_INIT;
int invoked_hook;
@@ -1453,6 +1454,7 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w
retval = push_to_deploy(sha1, &env, worktree->path);
strvec_clear(&env);
+ free(git_dir);
return retval;
}
diff --git a/builtin/refs.c b/builtin/refs.c
index c459507d51..998d2a2c1c 100644
--- a/builtin/refs.c
+++ b/builtin/refs.c
@@ -8,7 +8,7 @@
#include "worktree.h"
#define REFS_MIGRATE_USAGE \
- N_("git refs migrate --ref-format=<format> [--dry-run]")
+ N_("git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]")
#define REFS_VERIFY_USAGE \
N_("git refs verify [--strict] [--verbose]")
@@ -91,7 +91,7 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
git_config(git_fsck_config, &fsck_refs_options);
prepare_repo_settings(the_repository);
- worktrees = get_worktrees();
+ worktrees = get_worktrees_without_reading_head();
for (size_t i = 0; worktrees[i]; i++)
ret |= refs_fsck(get_worktree_ref_store(worktrees[i]),
&fsck_refs_options, worktrees[i]);
diff --git a/builtin/remote.c b/builtin/remote.c
index 816d482340..1b7aad8838 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -644,9 +644,11 @@ static int migrate_file(struct remote *remote)
git_config_set_multivar(buf.buf, remote->fetch.items[i].raw, "^$", 0);
#ifndef WITH_BREAKING_CHANGES
if (remote->origin == REMOTE_REMOTES)
- unlink_or_warn(git_path("remotes/%s", remote->name));
+ unlink_or_warn(repo_git_path_replace(the_repository, &buf,
+ "remotes/%s", remote->name));
else if (remote->origin == REMOTE_BRANCHES)
- unlink_or_warn(git_path("branches/%s", remote->name));
+ unlink_or_warn(repo_git_path_replace(the_repository, &buf,
+ "branches/%s", remote->name));
#endif /* WITH_BREAKING_CHANGES */
strbuf_release(&buf);
diff --git a/builtin/repack.c b/builtin/repack.c
index 75e3752353..f3330ade7b 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -1022,29 +1022,13 @@ static int write_filtered_pack(const struct pack_objects_args *args,
return finish_pack_objects_cmd(&cmd, names, local);
}
-static int existing_cruft_pack_cmp(const void *va, const void *vb)
+static void combine_small_cruft_packs(FILE *in, size_t combine_cruft_below_size,
+ struct existing_packs *existing)
{
- struct packed_git *a = *(struct packed_git **)va;
- struct packed_git *b = *(struct packed_git **)vb;
-
- if (a->pack_size < b->pack_size)
- return -1;
- if (a->pack_size > b->pack_size)
- return 1;
- return 0;
-}
-
-static void collapse_small_cruft_packs(FILE *in, size_t max_size,
- struct existing_packs *existing)
-{
- struct packed_git **existing_cruft, *p;
+ struct packed_git *p;
struct strbuf buf = STRBUF_INIT;
- size_t total_size = 0;
- size_t existing_cruft_nr = 0;
size_t i;
- ALLOC_ARRAY(existing_cruft, existing->cruft_packs.nr);
-
for (p = get_all_packs(the_repository); p; p = p->next) {
if (!(p->is_cruft && p->pack_local))
continue;
@@ -1056,24 +1040,7 @@ static void collapse_small_cruft_packs(FILE *in, size_t max_size,
if (!string_list_has_string(&existing->cruft_packs, buf.buf))
continue;
- if (existing_cruft_nr >= existing->cruft_packs.nr)
- BUG("too many cruft packs (found %"PRIuMAX", but knew "
- "of %"PRIuMAX")",
- (uintmax_t)existing_cruft_nr + 1,
- (uintmax_t)existing->cruft_packs.nr);
- existing_cruft[existing_cruft_nr++] = p;
- }
-
- QSORT(existing_cruft, existing_cruft_nr, existing_cruft_pack_cmp);
-
- for (i = 0; i < existing_cruft_nr; i++) {
- size_t proposed;
-
- p = existing_cruft[i];
- proposed = st_add(total_size, p->pack_size);
-
- if (proposed <= max_size) {
- total_size = proposed;
+ if (p->pack_size < combine_cruft_below_size) {
fprintf(in, "-%s\n", pack_basename(p));
} else {
retain_cruft_pack(existing, p);
@@ -1086,13 +1053,13 @@ static void collapse_small_cruft_packs(FILE *in, size_t max_size,
existing->non_kept_packs.items[i].string);
strbuf_release(&buf);
- free(existing_cruft);
}
static int write_cruft_pack(const struct pack_objects_args *args,
const char *destination,
const char *pack_prefix,
const char *cruft_expiration,
+ unsigned long combine_cruft_below_size,
struct string_list *names,
struct existing_packs *existing)
{
@@ -1135,8 +1102,9 @@ static int write_cruft_pack(const struct pack_objects_args *args,
in = xfdopen(cmd.in, "w");
for_each_string_list_item(item, names)
fprintf(in, "%s-%s.pack\n", pack_prefix, item->string);
- if (args->max_pack_size && !cruft_expiration) {
- collapse_small_cruft_packs(in, args->max_pack_size, existing);
+ if (combine_cruft_below_size && !cruft_expiration) {
+ combine_small_cruft_packs(in, combine_cruft_below_size,
+ existing);
} else {
for_each_string_list_item(item, &existing->non_kept_packs)
fprintf(in, "-%s.pack\n", item->string);
@@ -1190,6 +1158,7 @@ int cmd_repack(int argc,
const char *opt_window_memory = NULL;
const char *opt_depth = NULL;
const char *opt_threads = NULL;
+ unsigned long combine_cruft_below_size = 0ul;
struct option builtin_repack_options[] = {
OPT_BIT('a', NULL, &pack_everything,
@@ -1202,6 +1171,9 @@ int cmd_repack(int argc,
PACK_CRUFT),
OPT_STRING(0, "cruft-expiration", &cruft_expiration, N_("approxidate"),
N_("with --cruft, expire objects older than this")),
+ OPT_MAGNITUDE(0, "combine-cruft-below-size",
+ &combine_cruft_below_size,
+ N_("with --cruft, only repack cruft packs smaller than this")),
OPT_MAGNITUDE(0, "max-cruft-size", &cruft_po_args.max_pack_size,
N_("with --cruft, limit the size of new cruft packs")),
OPT_BOOL('d', NULL, &delete_redundant,
@@ -1445,7 +1417,8 @@ int cmd_repack(int argc,
cruft_po_args.quiet = po_args.quiet;
ret = write_cruft_pack(&cruft_po_args, packtmp, pack_prefix,
- cruft_expiration, &names,
+ cruft_expiration,
+ combine_cruft_below_size, &names,
&existing);
if (ret)
goto cleanup;
@@ -1472,10 +1445,17 @@ int cmd_repack(int argc,
* generate an empty pack (since every object not in the
* cruft pack generated above will have an mtime older
* than the expiration).
+ *
+ * Pretend we don't have a `--combine-cruft-below-size`
+ * argument, since we're not selectively combining
+ * anything based on size to generate the limbo cruft
+ * pack, but rather removing all cruft packs from the
+ * main repository regardless of size.
*/
ret = write_cruft_pack(&cruft_po_args, expire_to,
pack_prefix,
NULL,
+ 0ul,
&names,
&existing);
if (ret)
diff --git a/builtin/replace.c b/builtin/replace.c
index a4eaadff91..15ec0922ce 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -345,7 +345,7 @@ static int edit_and_replace(const char *object_ref, int force, int raw)
}
strbuf_release(&ref);
- tmpfile = git_pathdup("REPLACE_EDITOBJ");
+ tmpfile = repo_git_path(the_repository, "REPLACE_EDITOBJ");
if (export_object(&old_oid, type, raw, tmpfile)) {
free(tmpfile);
return -1;
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 41127e24e5..1312e79d89 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -4,9 +4,9 @@
#include "config.h"
#include "gettext.h"
#include "parse-options.h"
-
-#include "string-list.h"
#include "rerere.h"
+#include "strbuf.h"
+#include "string-list.h"
#include "xdiff/xdiff.h"
#include "xdiff-interface.h"
#include "pathspec.h"
@@ -112,15 +112,18 @@ int cmd_rerere(int argc,
merge_rr.items[i].util = NULL;
}
} else if (!strcmp(argv[0], "diff")) {
+ struct strbuf buf = STRBUF_INIT;
if (setup_rerere(the_repository, &merge_rr,
flags | RERERE_READONLY) < 0)
return 0;
for (size_t i = 0; i < merge_rr.nr; i++) {
const char *path = merge_rr.items[i].string;
const struct rerere_id *id = merge_rr.items[i].util;
- if (diff_two(rerere_path(id, "preimage"), path, path, path))
- die(_("unable to generate diff for '%s'"), rerere_path(id, NULL));
+ if (diff_two(rerere_path(&buf, id, "preimage"), path, path, path))
+ die(_("unable to generate diff for '%s'"), rerere_path(&buf, id, NULL));
}
+
+ strbuf_release(&buf);
} else
usage_with_options(rerere_usage, options);
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 428c866c05..490da33bec 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -789,8 +789,8 @@ int cmd_rev_parse(int argc,
if (!strcmp(arg, "--git-path")) {
if (!argv[i + 1])
die(_("--git-path requires an argument"));
- strbuf_reset(&buf);
- print_path(git_path("%s", argv[i + 1]), prefix,
+ print_path(repo_git_path_replace(the_repository, &buf,
+ "%s", argv[i + 1]), prefix,
format,
DEFAULT_RELATIVE_IF_SHARED);
i++;
@@ -1083,7 +1083,7 @@ int cmd_rev_parse(int argc,
die(_("Could not read the index"));
if (the_repository->index->split_index) {
const struct object_id *oid = &the_repository->index->split_index->base_oid;
- const char *path = git_path("sharedindex.%s", oid_to_hex(oid));
+ const char *path = repo_git_path_replace(the_repository, &buf, "sharedindex.%s", oid_to_hex(oid));
print_path(path, prefix, format, DEFAULT_RELATIVE);
}
continue;
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 8d461008e2..c6e0e9d051 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
#include "builtin.h"
#include "config.h"
#include "hex.h"
@@ -151,7 +150,7 @@ static int send_pack_config(const char *k, const char *v,
int cmd_send_pack(int argc,
const char **argv,
const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
struct refspec rs = REFSPEC_INIT_PUSH;
const char *remote_name = NULL;
@@ -212,7 +211,7 @@ int cmd_send_pack(int argc,
OPT_END()
};
- git_config(send_pack_config, NULL);
+ repo_config(repo, send_pack_config, NULL);
argc = parse_options(argc, argv, prefix, options, send_pack_usage, 0);
if (argc > 0) {
dest = argv[0];
@@ -317,7 +316,7 @@ int cmd_send_pack(int argc,
set_ref_status_for_push(remote_refs, args.send_mirror,
args.force_update);
- ret = send_pack(the_repository, &args, fd, conn, remote_refs, &extra_have);
+ ret = send_pack(repo, &args, fd, conn, remote_refs, &extra_have);
if (helper_status)
print_helper_status(remote_refs);
diff --git a/builtin/stash.c b/builtin/stash.c
index dbaa999cf1..cfbd92852a 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -13,7 +13,6 @@
#include "lockfile.h"
#include "cache-tree.h"
#include "unpack-trees.h"
-#include "merge-recursive.h"
#include "merge-ort-wrappers.h"
#include "strvec.h"
#include "run-command.h"
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index f9b970f8a6..c1a8029714 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1301,7 +1301,7 @@ static void sync_submodule(const char *path, const char *prefix,
remote_key = xstrfmt("remote.%s.url", default_remote);
free(default_remote);
- submodule_to_gitdir(&sb, path);
+ submodule_to_gitdir(the_repository, &sb, path);
strbuf_addstr(&sb, "/config");
if (git_config_set_in_file_gently(sb.buf, remote_key, NULL, sub_origin_url))
@@ -1826,7 +1826,7 @@ static int clone_submodule(const struct module_clone_data *clone_data,
connect_work_tree_and_git_dir(clone_data_path, sm_gitdir, 0);
- p = git_pathdup_submodule(clone_data_path, "config");
+ p = repo_submodule_path(the_repository, clone_data_path, "config");
if (!p)
die(_("could not get submodule directory for '%s'"), clone_data_path);
diff --git a/builtin/tag.c b/builtin/tag.c
index e8a344b926..d3e0943b73 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -667,7 +667,7 @@ int cmd_tag(int argc,
if (create_tag_object) {
if (force_sign_annotate && !annotate)
opt.sign = 1;
- path = git_pathdup("TAG_EDITMSG");
+ path = repo_git_path(the_repository, "TAG_EDITMSG");
create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object,
&trailer_args, path);
}
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 8383bcf404..c5a6dca856 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -668,6 +668,7 @@ int cmd_unpack_objects(int argc,
the_hash_algo->init_fn(&ctx);
unpack_all();
git_hash_update(&ctx, buffer, offset);
+ the_hash_algo->init_fn(&tmp_ctx);
git_hash_clone(&tmp_ctx, &ctx);
git_hash_final_oid(&oid, &tmp_ctx);
if (strict) {
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 4d35bdc4b4..1d541e13ad 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -179,7 +179,8 @@ static int parse_next_oid(const char **next, const char *end,
(*next)++;
*next = parse_arg(*next, &arg);
if (arg.len) {
- if (repo_get_oid(the_repository, arg.buf, oid))
+ if (repo_get_oid_with_flags(the_repository, arg.buf, oid,
+ GET_OID_SKIP_AMBIGUITY_CHECK))
goto invalid;
} else {
/* Without -z, an empty value means all zeros: */
@@ -197,7 +198,8 @@ static int parse_next_oid(const char **next, const char *end,
*next += arg.len;
if (arg.len) {
- if (repo_get_oid(the_repository, arg.buf, oid))
+ if (repo_get_oid_with_flags(the_repository, arg.buf, oid,
+ GET_OID_SKIP_AMBIGUITY_CHECK))
goto invalid;
} else if (flags & PARSE_SHA1_ALLOW_EMPTY) {
/* With -z, treat an empty value as all zeros: */
@@ -299,7 +301,8 @@ static void parse_cmd_symref_update(struct ref_transaction *transaction,
die("symref-update %s: expected old value", refname);
if (!strcmp(old_arg, "oid")) {
- if (repo_get_oid(the_repository, old_target, &old_oid))
+ if (repo_get_oid_with_flags(the_repository, old_target, &old_oid,
+ GET_OID_SKIP_AMBIGUITY_CHECK))
die("symref-update %s: invalid oid: %s", refname, old_target);
have_old_oid = 1;
@@ -772,7 +775,8 @@ int cmd_update_ref(int argc,
refname = argv[0];
value = argv[1];
oldval = argv[2];
- if (repo_get_oid(the_repository, value, &oid))
+ if (repo_get_oid_with_flags(the_repository, value, &oid,
+ GET_OID_SKIP_AMBIGUITY_CHECK))
die("%s: not a valid SHA1", value);
}
@@ -783,7 +787,8 @@ int cmd_update_ref(int argc,
* must not already exist:
*/
oidclr(&oldoid, the_repository->hash_algo);
- else if (repo_get_oid(the_repository, oldval, &oldoid))
+ else if (repo_get_oid_with_flags(the_repository, oldval, &oldoid,
+ GET_OID_SKIP_AMBIGUITY_CHECK))
die("%s: not a valid old SHA1", oldval);
}
diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c
index 779b7988ca..5f749a30da 100644
--- a/builtin/verify-commit.c
+++ b/builtin/verify-commit.c
@@ -5,7 +5,6 @@
*
* Based on git-verify-tag
*/
-#define USE_THE_REPOSITORY_VARIABLE
#include "builtin.h"
#include "config.h"
#include "gettext.h"
@@ -33,15 +32,15 @@ static int run_gpg_verify(struct commit *commit, unsigned flags)
return ret;
}
-static int verify_commit(const char *name, unsigned flags)
+static int verify_commit(struct repository *repo, const char *name, unsigned flags)
{
struct object_id oid;
struct object *obj;
- if (repo_get_oid(the_repository, name, &oid))
+ if (repo_get_oid(repo, name, &oid))
return error("commit '%s' not found.", name);
- obj = parse_object(the_repository, &oid);
+ obj = parse_object(repo, &oid);
if (!obj)
return error("%s: unable to read file.", name);
if (obj->type != OBJ_COMMIT)
@@ -54,7 +53,7 @@ static int verify_commit(const char *name, unsigned flags)
int cmd_verify_commit(int argc,
const char **argv,
const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
int i = 1, verbose = 0, had_error = 0;
unsigned flags = 0;
@@ -64,7 +63,7 @@ int cmd_verify_commit(int argc,
OPT_END()
};
- git_config(git_default_config, NULL);
+ repo_config(repo, git_default_config, NULL);
argc = parse_options(argc, argv, prefix, verify_commit_options,
verify_commit_usage, PARSE_OPT_KEEP_ARGV0);
@@ -78,7 +77,7 @@ int cmd_verify_commit(int argc,
* was received in the process of writing the gpg input: */
signal(SIGPIPE, SIG_IGN);
while (i < argc)
- if (verify_commit(argv[i++], flags))
+ if (verify_commit(repo, argv[i++], flags))
had_error = 1;
return had_error;
}
diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c
index f6b97048a5..ed1c40338f 100644
--- a/builtin/verify-tag.c
+++ b/builtin/verify-tag.c
@@ -5,7 +5,6 @@
*
* Based on git-verify-tag.sh
*/
-#define USE_THE_REPOSITORY_VARIABLE
#include "builtin.h"
#include "config.h"
#include "gettext.h"
@@ -23,7 +22,7 @@ static const char * const verify_tag_usage[] = {
int cmd_verify_tag(int argc,
const char **argv,
const char *prefix,
- struct repository *repo UNUSED)
+ struct repository *repo)
{
int i = 1, verbose = 0, had_error = 0;
unsigned flags = 0;
@@ -35,7 +34,7 @@ int cmd_verify_tag(int argc,
OPT_END()
};
- git_config(git_default_config, NULL);
+ repo_config(repo, git_default_config, NULL);
argc = parse_options(argc, argv, prefix, verify_tag_options,
verify_tag_usage, PARSE_OPT_KEEP_ARGV0);
@@ -56,7 +55,7 @@ int cmd_verify_tag(int argc,
struct object_id oid;
const char *name = argv[i++];
- if (repo_get_oid(the_repository, name, &oid)) {
+ if (repo_get_oid(repo, name, &oid)) {
had_error = !!error("tag '%s' not found.", name);
continue;
}
diff --git a/builtin/worktree.c b/builtin/worktree.c
index c043d4d523..48448a8355 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -151,7 +151,7 @@ static int delete_git_dir(const char *id)
struct strbuf sb = STRBUF_INIT;
int ret;
- strbuf_addstr(&sb, git_common_path("worktrees/%s", id));
+ repo_common_path_append(the_repository, &sb, "worktrees/%s", id);
ret = remove_dir_recursively(&sb, 0);
if (ret < 0 && errno == ENOTDIR)
ret = unlink(sb.buf);
@@ -163,7 +163,9 @@ static int delete_git_dir(const char *id)
static void delete_worktrees_dir_if_empty(void)
{
- rmdir(git_path("worktrees")); /* ignore failed removal */
+ char *path = repo_git_path(the_repository, "worktrees");
+ rmdir(path); /* ignore failed removal */
+ free(path);
}
static void prune_worktree(const char *id, const char *reason)
@@ -212,8 +214,13 @@ static void prune_worktrees(void)
struct strbuf reason = STRBUF_INIT;
struct strbuf main_path = STRBUF_INIT;
struct string_list kept = STRING_LIST_INIT_DUP;
- DIR *dir = opendir(git_path("worktrees"));
+ char *path;
+ DIR *dir;
struct dirent *d;
+
+ path = repo_git_path(the_repository, "worktrees");
+ dir = opendir(path);
+ free(path);
if (!dir)
return;
while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
@@ -337,7 +344,7 @@ static void check_candidate_path(const char *path,
static void copy_sparse_checkout(const char *worktree_git_dir)
{
- char *from_file = git_pathdup("info/sparse-checkout");
+ char *from_file = repo_git_path(the_repository, "info/sparse-checkout");
char *to_file = xstrfmt("%s/info/sparse-checkout", worktree_git_dir);
if (file_exists(from_file)) {
@@ -353,7 +360,7 @@ static void copy_sparse_checkout(const char *worktree_git_dir)
static void copy_filtered_worktree_config(const char *worktree_git_dir)
{
- char *from_file = git_pathdup("config.worktree");
+ char *from_file = repo_git_path(the_repository, "config.worktree");
char *to_file = xstrfmt("%s/config.worktree", worktree_git_dir);
if (file_exists(from_file)) {
@@ -457,7 +464,7 @@ static int add_worktree(const char *path, const char *refname,
BUG("How come '%s' becomes empty after sanitization?", sb.buf);
strbuf_reset(&sb);
name = sb_name.buf;
- git_path_buf(&sb_repo, "worktrees/%s", name);
+ repo_git_path_replace(the_repository, &sb_repo, "worktrees/%s", name);
len = sb_repo.len;
if (safe_create_leading_directories_const(sb_repo.buf))
die_errno(_("could not create leading directories of '%s'"),
@@ -657,8 +664,9 @@ static int can_use_local_refs(const struct add_opts *opts)
if (!opts->quiet) {
struct strbuf path = STRBUF_INIT;
struct strbuf contents = STRBUF_INIT;
+ char *wt_gitdir = get_worktree_git_dir(NULL);
- strbuf_add_real_path(&path, get_worktree_git_dir(NULL));
+ strbuf_add_real_path(&path, wt_gitdir);
strbuf_addstr(&path, "/HEAD");
strbuf_read_file(&contents, path.buf, 64);
strbuf_stripspace(&contents, NULL);
@@ -670,6 +678,7 @@ static int can_use_local_refs(const struct add_opts *opts)
path.buf, contents.buf);
strbuf_release(&path);
strbuf_release(&contents);
+ free(wt_gitdir);
}
return 1;
}
@@ -1100,6 +1109,7 @@ static int lock_worktree(int ac, const char **av, const char *prefix,
OPT_END()
};
struct worktree **worktrees, *wt;
+ char *path;
ac = parse_options(ac, av, prefix, options, git_worktree_lock_usage, 0);
if (ac != 1)
@@ -1120,9 +1130,11 @@ static int lock_worktree(int ac, const char **av, const char *prefix,
die(_("'%s' is already locked"), av[0]);
}
- write_file(git_common_path("worktrees/%s/locked", wt->id),
- "%s", reason);
+ path = repo_common_path(the_repository, "worktrees/%s/locked", wt->id);
+ write_file(path, "%s", reason);
+
free_worktrees(worktrees);
+ free(path);
return 0;
}
@@ -1133,6 +1145,7 @@ static int unlock_worktree(int ac, const char **av, const char *prefix,
OPT_END()
};
struct worktree **worktrees, *wt;
+ char *path;
int ret;
ac = parse_options(ac, av, prefix, options, git_worktree_unlock_usage, 0);
@@ -1147,8 +1160,12 @@ static int unlock_worktree(int ac, const char **av, const char *prefix,
die(_("The main working tree cannot be locked or unlocked"));
if (!worktree_lock_reason(wt))
die(_("'%s' is not locked"), av[0]);
- ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id));
+
+ path = repo_common_path(the_repository, "worktrees/%s/locked", wt->id);
+ ret = unlink_or_warn(path);
+
free_worktrees(worktrees);
+ free(path);
return ret;
}
@@ -1157,6 +1174,9 @@ static void validate_no_submodules(const struct worktree *wt)
struct index_state istate = INDEX_STATE_INIT(the_repository);
struct strbuf path = STRBUF_INIT;
int i, found_submodules = 0;
+ char *wt_gitdir;
+
+ wt_gitdir = get_worktree_git_dir(wt);
if (is_directory(worktree_git_path(the_repository, wt, "modules"))) {
/*
@@ -1166,7 +1186,7 @@ static void validate_no_submodules(const struct worktree *wt)
*/
found_submodules = 1;
} else if (read_index_from(&istate, worktree_git_path(the_repository, wt, "index"),
- get_worktree_git_dir(wt)) > 0) {
+ wt_gitdir) > 0) {
for (i = 0; i < istate.cache_nr; i++) {
struct cache_entry *ce = istate.cache[i];
int err;
@@ -1185,6 +1205,7 @@ static void validate_no_submodules(const struct worktree *wt)
}
discard_index(&istate);
strbuf_release(&path);
+ free(wt_gitdir);
if (found_submodules)
die(_("working trees containing submodules cannot be moved or removed"));
diff --git a/bulk-checkin.c b/bulk-checkin.c
index 20f2da67b9..a5a3395188 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -3,7 +3,6 @@
*/
#define USE_THE_REPOSITORY_VARIABLE
-#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
#include "bulk-checkin.h"
@@ -56,7 +55,6 @@ static void flush_bulk_checkin_packfile(struct bulk_checkin_packfile *state)
{
unsigned char hash[GIT_MAX_RAWSZ];
struct strbuf packname = STRBUF_INIT;
- int i;
if (!state->f)
return;
@@ -82,7 +80,7 @@ static void flush_bulk_checkin_packfile(struct bulk_checkin_packfile *state)
finish_tmp_packfile(&packname, state->pack_tmp_name,
state->written, state->nr_written,
&state->pack_idx_opts, hash);
- for (i = 0; i < state->nr_written; i++)
+ for (uint32_t i = 0; i < state->nr_written; i++)
free(state->written[i]);
clear_exit:
@@ -131,14 +129,12 @@ static void flush_batch_fsync(void)
static int already_written(struct bulk_checkin_packfile *state, struct object_id *oid)
{
- int i;
-
/* The object may already exist in the repository */
if (repo_has_object_file(the_repository, oid))
return 1;
/* Might want to keep the list sorted */
- for (i = 0; i < state->nr_written; i++)
+ for (uint32_t i = 0; i < state->nr_written; i++)
if (oideq(&state->written[i]->oid, oid))
return 1;
@@ -182,13 +178,13 @@ static int stream_blob_to_pack(struct bulk_checkin_packfile *state,
while (status != Z_STREAM_END) {
if (size && !s.avail_in) {
- ssize_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf);
+ size_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf);
ssize_t read_result = read_in_full(fd, ibuf, rsize);
if (read_result < 0)
die_errno("failed to read from '%s'", path);
- if (read_result != rsize)
- die("failed to read %d bytes from '%s'",
- (int)rsize, path);
+ if ((size_t)read_result != rsize)
+ die("failed to read %u bytes from '%s'",
+ (unsigned)rsize, path);
offset += rsize;
if (*already_hashed_to < offset) {
size_t hsize = offset - *already_hashed_to;
diff --git a/ci/check-unsafe-assertions.sh b/ci/check-unsafe-assertions.sh
new file mode 100755
index 0000000000..233bd9dfbc
--- /dev/null
+++ b/ci/check-unsafe-assertions.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+make CHECK_ASSERTION_SIDE_EFFECTS=1 >compiler_output 2>compiler_error
+if test $? != 0
+then
+ echo >&2 "ERROR: The compiler could not verify the following assert()"
+ echo >&2 " calls are free of side-effects. Please replace with"
+ echo >&2 " ASSERT() calls."
+ grep undefined.reference.to..not_supposed_to_survive compiler_error |
+ sed -e s/:[^:]*$// | sort | uniq | tr ':' ' ' |
+ while read f l
+ do
+ printf "${f}:${l}\n "
+ awk -v start="$l" 'NR >= start { print; if (/\);/) exit }' $f
+ done
+ exit 1
+fi
+rm compiler_output compiler_error
diff --git a/ci/run-static-analysis.sh b/ci/run-static-analysis.sh
index 0d51e5ce0e..ae714e020a 100755
--- a/ci/run-static-analysis.sh
+++ b/ci/run-static-analysis.sh
@@ -31,4 +31,6 @@ exit 1
make check-pot
+${0%/*}/check-unsafe-assertions.sh
+
save_good_tree
diff --git a/ci/test-documentation.sh b/ci/test-documentation.sh
index 6c018b673e..49f87f50fd 100755
--- a/ci/test-documentation.sh
+++ b/ci/test-documentation.sh
@@ -15,6 +15,13 @@ filter_log () {
"$1"
}
+check_docs () {
+ test -s "$1"/Documentation/git.html &&
+ test -s "$1"/Documentation/git.xml &&
+ test -s "$1"/Documentation/git.1 &&
+ grep "<meta name=\"generator\" content=\"$2 " "$1"/Documentation/git.html
+}
+
make check-builtins
make check-docs
@@ -23,10 +30,7 @@ make doc > >(tee stdout.log) 2> >(tee stderr.raw >&2)
cat stderr.raw
filter_log stderr.raw >stderr.log
test ! -s stderr.log
-test -s Documentation/git.html
-test -s Documentation/git.xml
-test -s Documentation/git.1
-grep '<meta name="generator" content="AsciiDoc ' Documentation/git.html
+check_docs . AsciiDoc
rm -f stdout.log stderr.log stderr.raw
check_unignored_build_artifacts
@@ -37,10 +41,21 @@ make USE_ASCIIDOCTOR=1 doc > >(tee stdout.log) 2> >(tee stderr.raw >&2)
cat stderr.raw
filter_log stderr.raw >stderr.log
test ! -s stderr.log
-test -s Documentation/git.html
-grep '<meta name="generator" content="Asciidoctor ' Documentation/git.html
+check_docs . Asciidoctor
rm -f stdout.log stderr.log stderr.raw
check_unignored_build_artifacts
+# Build docs with Meson and AsciiDoc
+meson setup build-asciidoc -Ddocs=html,man -Ddocs_backend=asciidoc
+meson compile -C build-asciidoc
+check_docs build-asciidoc AsciiDoc
+rm -rf build-asciidoc
+
+# Build docs with Meson and AsciiDoctor
+meson setup build-asciidoctor -Ddocs=html,man -Ddocs_backend=asciidoctor
+meson compile -C build-asciidoctor
+check_docs build-asciidoctor Asciidoctor
+rm -rf build-asciidoctor
+
save_good_tree
diff --git a/command-list.txt b/command-list.txt
index c537114b46..b7ade3ab9f 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -96,6 +96,7 @@ git-diagnose ancillaryinterrogators
git-diff mainporcelain info
git-diff-files plumbinginterrogators
git-diff-index plumbinginterrogators
+git-diff-pairs plumbinginterrogators
git-diff-tree plumbinginterrogators
git-difftool ancillaryinterrogators complete
git-fast-export ancillarymanipulators
diff --git a/commit-graph.c b/commit-graph.c
index 2a2999a6b8..1021ccb983 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -2084,7 +2084,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
return -1;
}
- if (adjust_shared_perm(get_tempfile_path(graph_layer))) {
+ if (adjust_shared_perm(the_repository, get_tempfile_path(graph_layer))) {
error(_("unable to adjust shared permissions for '%s'"),
get_tempfile_path(graph_layer));
return -1;
diff --git a/commit.c b/commit.c
index 6efdb03997..425503bb9f 100644
--- a/commit.c
+++ b/commit.c
@@ -780,19 +780,17 @@ static void clear_commit_marks_1(struct commit_list **plist,
void clear_commit_marks_many(size_t nr, struct commit **commit, unsigned int mark)
{
- for (size_t i = 0; i < nr; i++) {
- struct commit_list *list = NULL;
-
- clear_commit_marks_1(&list, *commit, mark);
- while (list)
- clear_commit_marks_1(&list, pop_commit(&list), mark);
- commit++;
- }
+ for (size_t i = 0; i < nr; i++)
+ clear_commit_marks(commit[i], mark);
}
void clear_commit_marks(struct commit *commit, unsigned int mark)
{
- clear_commit_marks_many(1, &commit, mark);
+ struct commit_list *list = NULL;
+
+ clear_commit_marks_1(&list, commit, mark);
+ while (list)
+ clear_commit_marks_1(&list, pop_commit(&list), mark);
}
struct commit *pop_commit(struct commit_list **stack)
diff --git a/compat/mingw-posix.h b/compat/mingw-posix.h
new file mode 100644
index 0000000000..88e0cf9292
--- /dev/null
+++ b/compat/mingw-posix.h
@@ -0,0 +1,435 @@
+#ifndef COMPAT_MINGW_POSIX_H
+#define COMPAT_MINGW_POSIX_H
+
+#ifdef __MINGW64_VERSION_MAJOR
+#include <stdint.h>
+#include <wchar.h>
+typedef _sigset_t sigset_t;
+#endif
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+/* MinGW-w64 reports to have flockfile, but it does not actually have it. */
+#ifdef __MINGW64_VERSION_MAJOR
+#undef _POSIX_THREAD_SAFE_FUNCTIONS
+#endif
+
+/*
+ * things that are not available in header files
+ */
+
+typedef int uid_t;
+typedef int socklen_t;
+#ifndef __MINGW64_VERSION_MAJOR
+typedef int pid_t;
+#define hstrerror strerror
+#endif
+
+#define S_IFLNK 0120000 /* Symbolic link */
+#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
+#define S_ISSOCK(x) 0
+
+#ifndef S_IRWXG
+#define S_IRGRP 0
+#define S_IWGRP 0
+#define S_IXGRP 0
+#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
+#endif
+#ifndef S_IRWXO
+#define S_IROTH 0
+#define S_IWOTH 0
+#define S_IXOTH 0
+#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
+#endif
+
+#define S_ISUID 0004000
+#define S_ISGID 0002000
+#define S_ISVTX 0001000
+
+#define WIFEXITED(x) 1
+#define WIFSIGNALED(x) 0
+#define WEXITSTATUS(x) ((x) & 0xff)
+#define WTERMSIG(x) SIGTERM
+
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK EAGAIN
+#endif
+#ifndef ELOOP
+#define ELOOP EMLINK
+#endif
+#define SHUT_WR SD_SEND
+
+#define SIGHUP 1
+#define SIGQUIT 3
+#define SIGKILL 9
+#define SIGPIPE 13
+#define SIGALRM 14
+#define SIGCHLD 17
+
+#define F_GETFD 1
+#define F_SETFD 2
+#define FD_CLOEXEC 0x1
+
+#if !defined O_CLOEXEC && defined O_NOINHERIT
+#define O_CLOEXEC O_NOINHERIT
+#endif
+
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#endif
+#ifndef ECONNABORTED
+#define ECONNABORTED WSAECONNABORTED
+#endif
+#ifndef ENOTSOCK
+#define ENOTSOCK WSAENOTSOCK
+#endif
+
+struct passwd {
+ char *pw_name;
+ char *pw_gecos;
+ char *pw_dir;
+};
+
+typedef void (__cdecl *sig_handler_t)(int);
+struct sigaction {
+ sig_handler_t sa_handler;
+ unsigned sa_flags;
+};
+#define SA_RESTART 0
+
+struct itimerval {
+ struct timeval it_value, it_interval;
+};
+#define ITIMER_REAL 0
+
+struct utsname {
+ char sysname[16];
+ char nodename[1];
+ char release[16];
+ char version[16];
+ char machine[1];
+};
+
+/*
+ * sanitize preprocessor namespace polluted by Windows headers defining
+ * macros which collide with git local versions
+ */
+#undef HELP_COMMAND /* from winuser.h */
+
+/*
+ * trivial stubs
+ */
+
+static inline int readlink(const char *path UNUSED, char *buf UNUSED, size_t bufsiz UNUSED)
+{ errno = ENOSYS; return -1; }
+static inline int symlink(const char *oldpath UNUSED, const char *newpath UNUSED)
+{ errno = ENOSYS; return -1; }
+static inline int fchmod(int fildes UNUSED, mode_t mode UNUSED)
+{ errno = ENOSYS; return -1; }
+#ifndef __MINGW64_VERSION_MAJOR
+static inline pid_t fork(void)
+{ errno = ENOSYS; return -1; }
+#endif
+static inline unsigned int alarm(unsigned int seconds UNUSED)
+{ return 0; }
+static inline int fsync(int fd)
+{ return _commit(fd); }
+static inline void sync(void)
+{}
+static inline uid_t getuid(void)
+{ return 1; }
+static inline struct passwd *getpwnam(const char *name UNUSED)
+{ return NULL; }
+static inline int fcntl(int fd UNUSED, int cmd, ...)
+{
+ if (cmd == F_GETFD || cmd == F_SETFD)
+ return 0;
+ errno = EINVAL;
+ return -1;
+}
+
+#define sigemptyset(x) (void)0
+static inline int sigaddset(sigset_t *set UNUSED, int signum UNUSED)
+{ return 0; }
+#define SIG_BLOCK 0
+#define SIG_UNBLOCK 0
+static inline int sigprocmask(int how UNUSED, const sigset_t *set UNUSED, sigset_t *oldset UNUSED)
+{ return 0; }
+static inline pid_t getppid(void)
+{ return 1; }
+static inline pid_t getpgid(pid_t pid)
+{ return pid == 0 ? getpid() : pid; }
+static inline pid_t tcgetpgrp(int fd UNUSED)
+{ return getpid(); }
+
+/*
+ * simple adaptors
+ */
+
+int mingw_mkdir(const char *path, int mode);
+#define mkdir mingw_mkdir
+
+#define WNOHANG 1
+pid_t waitpid(pid_t pid, int *status, int options);
+
+#define kill mingw_kill
+int mingw_kill(pid_t pid, int sig);
+
+#define locate_in_PATH mingw_locate_in_PATH
+char *mingw_locate_in_PATH(const char *cmd);
+
+/*
+ * implementations of missing functions
+ */
+
+int pipe(int filedes[2]);
+unsigned int sleep (unsigned int seconds);
+int mkstemp(char *template);
+int gettimeofday(struct timeval *tv, void *tz);
+#ifndef __MINGW64_VERSION_MAJOR
+struct tm *gmtime_r(const time_t *timep, struct tm *result);
+struct tm *localtime_r(const time_t *timep, struct tm *result);
+#endif
+int getpagesize(void); /* defined in MinGW's libgcc.a */
+struct passwd *getpwuid(uid_t uid);
+int setitimer(int type, struct itimerval *in, struct itimerval *out);
+int sigaction(int sig, struct sigaction *in, struct sigaction *out);
+int link(const char *oldpath, const char *newpath);
+int uname(struct utsname *buf);
+
+/*
+ * replacements of existing functions
+ */
+
+int mingw_unlink(const char *pathname, int handle_in_use_error);
+#ifdef MINGW_DONT_HANDLE_IN_USE_ERROR
+# define unlink(path) mingw_unlink(path, 0)
+#else
+# define unlink(path) mingw_unlink(path, 1)
+#endif
+
+int mingw_rmdir(const char *path);
+#define rmdir mingw_rmdir
+
+int mingw_open (const char *filename, int oflags, ...);
+#define open mingw_open
+#undef OPEN_RETURNS_EINTR
+
+int mingw_fgetc(FILE *stream);
+#define fgetc mingw_fgetc
+
+FILE *mingw_fopen (const char *filename, const char *otype);
+#define fopen mingw_fopen
+
+FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream);
+#define freopen mingw_freopen
+
+int mingw_fflush(FILE *stream);
+#define fflush mingw_fflush
+
+ssize_t mingw_write(int fd, const void *buf, size_t len);
+#define write mingw_write
+
+int mingw_access(const char *filename, int mode);
+#undef access
+#define access mingw_access
+
+int mingw_chdir(const char *dirname);
+#define chdir mingw_chdir
+
+int mingw_chmod(const char *filename, int mode);
+#define chmod mingw_chmod
+
+char *mingw_mktemp(char *template);
+#define mktemp mingw_mktemp
+
+char *mingw_getcwd(char *pointer, int len);
+#define getcwd mingw_getcwd
+
+#ifdef NO_UNSETENV
+#error "NO_UNSETENV is incompatible with the Windows-specific startup code!"
+#endif
+
+/*
+ * We bind *env() routines (even the mingw_ ones) to private mingw_ versions.
+ * These talk to the CRT using UNICODE/wchar_t, but maintain the original
+ * narrow-char API.
+ *
+ * Note that the MSCRT maintains both ANSI (getenv()) and UNICODE (_wgetenv())
+ * routines and stores both versions of each environment variable in parallel
+ * (and secretly updates both when you set one or the other), but it uses CP_ACP
+ * to do the conversion rather than CP_UTF8.
+ *
+ * Since everything in the git code base is UTF8, we define the mingw_ routines
+ * to access the CRT using the UNICODE routines and manually convert them to
+ * UTF8. This also avoids round-trip problems.
+ *
+ * This also helps with our linkage, since "_wenviron" is publicly exported
+ * from the CRT. But to access "_environ" we would have to statically link
+ * to the CRT (/MT).
+ *
+ * We require NO_SETENV (and let gitsetenv() call our mingw_putenv).
+ */
+#define getenv mingw_getenv
+#define putenv mingw_putenv
+#define unsetenv mingw_putenv
+char *mingw_getenv(const char *name);
+int mingw_putenv(const char *name);
+
+int mingw_gethostname(char *host, int namelen);
+#define gethostname mingw_gethostname
+
+struct hostent *mingw_gethostbyname(const char *host);
+#define gethostbyname mingw_gethostbyname
+
+int mingw_getaddrinfo(const char *node, const char *service,
+ const struct addrinfo *hints, struct addrinfo **res);
+#define getaddrinfo mingw_getaddrinfo
+
+int mingw_socket(int domain, int type, int protocol);
+#define socket mingw_socket
+
+int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz);
+#define connect mingw_connect
+
+int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz);
+#define bind mingw_bind
+
+int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen);
+#define setsockopt mingw_setsockopt
+
+int mingw_shutdown(int sockfd, int how);
+#define shutdown mingw_shutdown
+
+int mingw_listen(int sockfd, int backlog);
+#define listen mingw_listen
+
+int mingw_accept(int sockfd, struct sockaddr *sa, socklen_t *sz);
+#define accept mingw_accept
+
+int mingw_rename(const char*, const char*);
+#define rename mingw_rename
+
+#if defined(USE_WIN32_MMAP) || defined(_MSC_VER)
+int mingw_getpagesize(void);
+#define getpagesize mingw_getpagesize
+#endif
+
+int win32_fsync_no_flush(int fd);
+#define fsync_no_flush win32_fsync_no_flush
+
+#define FSYNC_COMPONENTS_PLATFORM_DEFAULT (FSYNC_COMPONENTS_DEFAULT | FSYNC_COMPONENT_LOOSE_OBJECT)
+#define FSYNC_METHOD_DEFAULT (FSYNC_METHOD_BATCH)
+
+struct rlimit {
+ unsigned int rlim_cur;
+};
+#define RLIMIT_NOFILE 0
+
+static inline int getrlimit(int resource, struct rlimit *rlp)
+{
+ if (resource != RLIMIT_NOFILE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ rlp->rlim_cur = 2048;
+ return 0;
+}
+
+/*
+ * Use mingw specific stat()/lstat()/fstat() implementations on Windows,
+ * including our own struct stat with 64 bit st_size and nanosecond-precision
+ * file times.
+ */
+#ifndef __MINGW64_VERSION_MAJOR
+#define off_t off64_t
+#define lseek _lseeki64
+#ifndef _MSC_VER
+struct timespec {
+ time_t tv_sec;
+ long tv_nsec;
+};
+#endif
+#endif
+
+struct mingw_stat {
+ _dev_t st_dev;
+ _ino_t st_ino;
+ _mode_t st_mode;
+ short st_nlink;
+ short st_uid;
+ short st_gid;
+ _dev_t st_rdev;
+ off64_t st_size;
+ struct timespec st_atim;
+ struct timespec st_mtim;
+ struct timespec st_ctim;
+};
+
+#define st_atime st_atim.tv_sec
+#define st_mtime st_mtim.tv_sec
+#define st_ctime st_ctim.tv_sec
+
+#ifdef stat
+#undef stat
+#endif
+#define stat mingw_stat
+int mingw_lstat(const char *file_name, struct stat *buf);
+int mingw_stat(const char *file_name, struct stat *buf);
+int mingw_fstat(int fd, struct stat *buf);
+#ifdef fstat
+#undef fstat
+#endif
+#define fstat mingw_fstat
+#ifdef lstat
+#undef lstat
+#endif
+#define lstat mingw_lstat
+
+
+int mingw_utime(const char *file_name, const struct utimbuf *times);
+#define utime mingw_utime
+size_t mingw_strftime(char *s, size_t max,
+ const char *format, const struct tm *tm);
+#define strftime mingw_strftime
+
+pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
+ const char *dir,
+ int fhin, int fhout, int fherr);
+int mingw_execvp(const char *cmd, char *const *argv);
+#define execvp mingw_execvp
+int mingw_execv(const char *cmd, char *const *argv);
+#define execv mingw_execv
+
+static inline unsigned int git_ntohl(unsigned int x)
+{ return (unsigned int)ntohl(x); }
+#define ntohl git_ntohl
+
+sig_handler_t mingw_signal(int sig, sig_handler_t handler);
+#define signal mingw_signal
+
+int mingw_raise(int sig);
+#define raise mingw_raise
+
+/*
+ * ANSI emulation wrappers
+ */
+
+int winansi_isatty(int fd);
+#define isatty winansi_isatty
+
+int winansi_dup2(int oldfd, int newfd);
+#define dup2 winansi_dup2
+
+void winansi_init(void);
+HANDLE winansi_get_osfhandle(int fd);
+
+#if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
+#define PRIuMAX "I64u"
+#define PRId64 "I64d"
+#else
+#include <inttypes.h>
+#endif
+
+#endif /* COMPAT_MINGW_POSIX_H */
diff --git a/compat/mingw.c b/compat/mingw.c
index f524c54d06..6ea2e7a35b 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -302,7 +302,7 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
return wbuf;
}
-int mingw_unlink(const char *pathname)
+int mingw_unlink(const char *pathname, int handle_in_use_error)
{
int ret, tries = 0;
wchar_t wpathname[MAX_PATH];
@@ -317,6 +317,9 @@ int mingw_unlink(const char *pathname)
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
if (!is_file_in_use_error(GetLastError()))
break;
+ if (!handle_in_use_error)
+ return ret;
+
/*
* We assume that some other process had the source or
* destination file open at the wrong moment and retry.
@@ -2826,31 +2829,44 @@ static void setup_windows_environment(void)
}
}
-static PSID get_current_user_sid(void)
+static void get_current_user_sid(PSID *sid, HANDLE *linked_token)
{
HANDLE token;
DWORD len = 0;
- PSID result = NULL;
+ TOKEN_ELEVATION_TYPE elevationType;
+ DWORD size;
+
+ *sid = NULL;
+ *linked_token = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
- return NULL;
+ return;
if (!GetTokenInformation(token, TokenUser, NULL, 0, &len)) {
TOKEN_USER *info = xmalloc((size_t)len);
if (GetTokenInformation(token, TokenUser, info, len, &len)) {
len = GetLengthSid(info->User.Sid);
- result = xmalloc(len);
- if (!CopySid(len, result, info->User.Sid)) {
+ *sid = xmalloc(len);
+ if (!CopySid(len, *sid, info->User.Sid)) {
error(_("failed to copy SID (%ld)"),
GetLastError());
- FREE_AND_NULL(result);
+ FREE_AND_NULL(*sid);
}
}
FREE_AND_NULL(info);
}
- CloseHandle(token);
- return result;
+ if (GetTokenInformation(token, TokenElevationType, &elevationType, sizeof(elevationType), &size) &&
+ elevationType == TokenElevationTypeLimited) {
+ /*
+ * The current process is run by a member of the Administrators
+ * group, but is not running elevated.
+ */
+ if (!GetTokenInformation(token, TokenLinkedToken, linked_token, sizeof(*linked_token), &size))
+ linked_token = NULL; /* there is no linked token */
+ }
+
+ CloseHandle(token);
}
static BOOL user_sid_to_user_name(PSID sid, LPSTR *str)
@@ -2931,18 +2947,22 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
else if (sid && IsValidSid(sid)) {
/* Now, verify that the SID matches the current user's */
static PSID current_user_sid;
+ static HANDLE linked_token;
BOOL is_member;
if (!current_user_sid)
- current_user_sid = get_current_user_sid();
+ get_current_user_sid(&current_user_sid, &linked_token);
if (current_user_sid &&
IsValidSid(current_user_sid) &&
EqualSid(sid, current_user_sid))
result = 1;
else if (IsWellKnownSid(sid, WinBuiltinAdministratorsSid) &&
- CheckTokenMembership(NULL, sid, &is_member) &&
- is_member)
+ ((CheckTokenMembership(NULL, sid, &is_member) &&
+ is_member) ||
+ (linked_token &&
+ CheckTokenMembership(linked_token, sid, &is_member) &&
+ is_member)))
/*
* If owned by the Administrators group, and the
* current user is an administrator, we consider that
diff --git a/compat/mingw.h b/compat/mingw.h
index ebfb8ba423..444daedfa5 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -1,185 +1,10 @@
-#ifdef __MINGW64_VERSION_MAJOR
-#include <stdint.h>
-#include <wchar.h>
-typedef _sigset_t sigset_t;
-#endif
-#include <winsock2.h>
-#include <ws2tcpip.h>
-
-/* MinGW-w64 reports to have flockfile, but it does not actually have it. */
-#ifdef __MINGW64_VERSION_MAJOR
-#undef _POSIX_THREAD_SAFE_FUNCTIONS
-#endif
+#include "mingw-posix.h"
struct config_context;
int mingw_core_config(const char *var, const char *value,
const struct config_context *ctx, void *cb);
#define platform_core_config mingw_core_config
-/*
- * things that are not available in header files
- */
-
-typedef int uid_t;
-typedef int socklen_t;
-#ifndef __MINGW64_VERSION_MAJOR
-typedef int pid_t;
-#define hstrerror strerror
-#endif
-
-#define S_IFLNK 0120000 /* Symbolic link */
-#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
-#define S_ISSOCK(x) 0
-
-#ifndef S_IRWXG
-#define S_IRGRP 0
-#define S_IWGRP 0
-#define S_IXGRP 0
-#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
-#endif
-#ifndef S_IRWXO
-#define S_IROTH 0
-#define S_IWOTH 0
-#define S_IXOTH 0
-#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
-#endif
-
-#define S_ISUID 0004000
-#define S_ISGID 0002000
-#define S_ISVTX 0001000
-
-#define WIFEXITED(x) 1
-#define WIFSIGNALED(x) 0
-#define WEXITSTATUS(x) ((x) & 0xff)
-#define WTERMSIG(x) SIGTERM
-
-#ifndef EWOULDBLOCK
-#define EWOULDBLOCK EAGAIN
-#endif
-#ifndef ELOOP
-#define ELOOP EMLINK
-#endif
-#define SHUT_WR SD_SEND
-
-#define SIGHUP 1
-#define SIGQUIT 3
-#define SIGKILL 9
-#define SIGPIPE 13
-#define SIGALRM 14
-#define SIGCHLD 17
-
-#define F_GETFD 1
-#define F_SETFD 2
-#define FD_CLOEXEC 0x1
-
-#if !defined O_CLOEXEC && defined O_NOINHERIT
-#define O_CLOEXEC O_NOINHERIT
-#endif
-
-#ifndef EAFNOSUPPORT
-#define EAFNOSUPPORT WSAEAFNOSUPPORT
-#endif
-#ifndef ECONNABORTED
-#define ECONNABORTED WSAECONNABORTED
-#endif
-#ifndef ENOTSOCK
-#define ENOTSOCK WSAENOTSOCK
-#endif
-
-struct passwd {
- char *pw_name;
- char *pw_gecos;
- char *pw_dir;
-};
-
-typedef void (__cdecl *sig_handler_t)(int);
-struct sigaction {
- sig_handler_t sa_handler;
- unsigned sa_flags;
-};
-#define SA_RESTART 0
-
-struct itimerval {
- struct timeval it_value, it_interval;
-};
-#define ITIMER_REAL 0
-
-struct utsname {
- char sysname[16];
- char nodename[1];
- char release[16];
- char version[16];
- char machine[1];
-};
-
-/*
- * sanitize preprocessor namespace polluted by Windows headers defining
- * macros which collide with git local versions
- */
-#undef HELP_COMMAND /* from winuser.h */
-
-/*
- * trivial stubs
- */
-
-static inline int readlink(const char *path UNUSED, char *buf UNUSED, size_t bufsiz UNUSED)
-{ errno = ENOSYS; return -1; }
-static inline int symlink(const char *oldpath UNUSED, const char *newpath UNUSED)
-{ errno = ENOSYS; return -1; }
-static inline int fchmod(int fildes UNUSED, mode_t mode UNUSED)
-{ errno = ENOSYS; return -1; }
-#ifndef __MINGW64_VERSION_MAJOR
-static inline pid_t fork(void)
-{ errno = ENOSYS; return -1; }
-#endif
-static inline unsigned int alarm(unsigned int seconds UNUSED)
-{ return 0; }
-static inline int fsync(int fd)
-{ return _commit(fd); }
-static inline void sync(void)
-{}
-static inline uid_t getuid(void)
-{ return 1; }
-static inline struct passwd *getpwnam(const char *name UNUSED)
-{ return NULL; }
-static inline int fcntl(int fd UNUSED, int cmd, ...)
-{
- if (cmd == F_GETFD || cmd == F_SETFD)
- return 0;
- errno = EINVAL;
- return -1;
-}
-
-#define sigemptyset(x) (void)0
-static inline int sigaddset(sigset_t *set UNUSED, int signum UNUSED)
-{ return 0; }
-#define SIG_BLOCK 0
-#define SIG_UNBLOCK 0
-static inline int sigprocmask(int how UNUSED, const sigset_t *set UNUSED, sigset_t *oldset UNUSED)
-{ return 0; }
-static inline pid_t getppid(void)
-{ return 1; }
-static inline pid_t getpgid(pid_t pid)
-{ return pid == 0 ? getpid() : pid; }
-static inline pid_t tcgetpgrp(int fd UNUSED)
-{ return getpid(); }
-
-/*
- * simple adaptors
- */
-
-int mingw_mkdir(const char *path, int mode);
-#define mkdir mingw_mkdir
-
-#define WNOHANG 1
-pid_t waitpid(pid_t pid, int *status, int options);
-
-#define kill mingw_kill
-int mingw_kill(pid_t pid, int sig);
-
-#define locate_in_PATH mingw_locate_in_PATH
-char *mingw_locate_in_PATH(const char *cmd);
-
#ifndef NO_OPENSSL
#include <openssl/ssl.h>
static inline int mingw_SSL_set_fd(SSL *ssl, int fd)
@@ -202,249 +27,6 @@ static inline int mingw_SSL_set_wfd(SSL *ssl, int fd)
#endif
/*
- * implementations of missing functions
- */
-
-int pipe(int filedes[2]);
-unsigned int sleep (unsigned int seconds);
-int mkstemp(char *template);
-int gettimeofday(struct timeval *tv, void *tz);
-#ifndef __MINGW64_VERSION_MAJOR
-struct tm *gmtime_r(const time_t *timep, struct tm *result);
-struct tm *localtime_r(const time_t *timep, struct tm *result);
-#endif
-int getpagesize(void); /* defined in MinGW's libgcc.a */
-struct passwd *getpwuid(uid_t uid);
-int setitimer(int type, struct itimerval *in, struct itimerval *out);
-int sigaction(int sig, struct sigaction *in, struct sigaction *out);
-int link(const char *oldpath, const char *newpath);
-int uname(struct utsname *buf);
-
-/*
- * replacements of existing functions
- */
-
-int mingw_unlink(const char *pathname);
-#define unlink mingw_unlink
-
-int mingw_rmdir(const char *path);
-#define rmdir mingw_rmdir
-
-int mingw_open (const char *filename, int oflags, ...);
-#define open mingw_open
-#undef OPEN_RETURNS_EINTR
-
-int mingw_fgetc(FILE *stream);
-#define fgetc mingw_fgetc
-
-FILE *mingw_fopen (const char *filename, const char *otype);
-#define fopen mingw_fopen
-
-FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream);
-#define freopen mingw_freopen
-
-int mingw_fflush(FILE *stream);
-#define fflush mingw_fflush
-
-ssize_t mingw_write(int fd, const void *buf, size_t len);
-#define write mingw_write
-
-int mingw_access(const char *filename, int mode);
-#undef access
-#define access mingw_access
-
-int mingw_chdir(const char *dirname);
-#define chdir mingw_chdir
-
-int mingw_chmod(const char *filename, int mode);
-#define chmod mingw_chmod
-
-char *mingw_mktemp(char *template);
-#define mktemp mingw_mktemp
-
-char *mingw_getcwd(char *pointer, int len);
-#define getcwd mingw_getcwd
-
-#ifdef NO_UNSETENV
-#error "NO_UNSETENV is incompatible with the Windows-specific startup code!"
-#endif
-
-/*
- * We bind *env() routines (even the mingw_ ones) to private mingw_ versions.
- * These talk to the CRT using UNICODE/wchar_t, but maintain the original
- * narrow-char API.
- *
- * Note that the MSCRT maintains both ANSI (getenv()) and UNICODE (_wgetenv())
- * routines and stores both versions of each environment variable in parallel
- * (and secretly updates both when you set one or the other), but it uses CP_ACP
- * to do the conversion rather than CP_UTF8.
- *
- * Since everything in the git code base is UTF8, we define the mingw_ routines
- * to access the CRT using the UNICODE routines and manually convert them to
- * UTF8. This also avoids round-trip problems.
- *
- * This also helps with our linkage, since "_wenviron" is publicly exported
- * from the CRT. But to access "_environ" we would have to statically link
- * to the CRT (/MT).
- *
- * We require NO_SETENV (and let gitsetenv() call our mingw_putenv).
- */
-#define getenv mingw_getenv
-#define putenv mingw_putenv
-#define unsetenv mingw_putenv
-char *mingw_getenv(const char *name);
-int mingw_putenv(const char *name);
-
-int mingw_gethostname(char *host, int namelen);
-#define gethostname mingw_gethostname
-
-struct hostent *mingw_gethostbyname(const char *host);
-#define gethostbyname mingw_gethostbyname
-
-int mingw_getaddrinfo(const char *node, const char *service,
- const struct addrinfo *hints, struct addrinfo **res);
-#define getaddrinfo mingw_getaddrinfo
-
-int mingw_socket(int domain, int type, int protocol);
-#define socket mingw_socket
-
-int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz);
-#define connect mingw_connect
-
-int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz);
-#define bind mingw_bind
-
-int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen);
-#define setsockopt mingw_setsockopt
-
-int mingw_shutdown(int sockfd, int how);
-#define shutdown mingw_shutdown
-
-int mingw_listen(int sockfd, int backlog);
-#define listen mingw_listen
-
-int mingw_accept(int sockfd, struct sockaddr *sa, socklen_t *sz);
-#define accept mingw_accept
-
-int mingw_rename(const char*, const char*);
-#define rename mingw_rename
-
-#if defined(USE_WIN32_MMAP) || defined(_MSC_VER)
-int mingw_getpagesize(void);
-#define getpagesize mingw_getpagesize
-#endif
-
-int win32_fsync_no_flush(int fd);
-#define fsync_no_flush win32_fsync_no_flush
-
-#define FSYNC_COMPONENTS_PLATFORM_DEFAULT (FSYNC_COMPONENTS_DEFAULT | FSYNC_COMPONENT_LOOSE_OBJECT)
-#define FSYNC_METHOD_DEFAULT (FSYNC_METHOD_BATCH)
-
-struct rlimit {
- unsigned int rlim_cur;
-};
-#define RLIMIT_NOFILE 0
-
-static inline int getrlimit(int resource, struct rlimit *rlp)
-{
- if (resource != RLIMIT_NOFILE) {
- errno = EINVAL;
- return -1;
- }
-
- rlp->rlim_cur = 2048;
- return 0;
-}
-
-/*
- * Use mingw specific stat()/lstat()/fstat() implementations on Windows,
- * including our own struct stat with 64 bit st_size and nanosecond-precision
- * file times.
- */
-#ifndef __MINGW64_VERSION_MAJOR
-#define off_t off64_t
-#define lseek _lseeki64
-#ifndef _MSC_VER
-struct timespec {
- time_t tv_sec;
- long tv_nsec;
-};
-#endif
-#endif
-
-struct mingw_stat {
- _dev_t st_dev;
- _ino_t st_ino;
- _mode_t st_mode;
- short st_nlink;
- short st_uid;
- short st_gid;
- _dev_t st_rdev;
- off64_t st_size;
- struct timespec st_atim;
- struct timespec st_mtim;
- struct timespec st_ctim;
-};
-
-#define st_atime st_atim.tv_sec
-#define st_mtime st_mtim.tv_sec
-#define st_ctime st_ctim.tv_sec
-
-#ifdef stat
-#undef stat
-#endif
-#define stat mingw_stat
-int mingw_lstat(const char *file_name, struct stat *buf);
-int mingw_stat(const char *file_name, struct stat *buf);
-int mingw_fstat(int fd, struct stat *buf);
-#ifdef fstat
-#undef fstat
-#endif
-#define fstat mingw_fstat
-#ifdef lstat
-#undef lstat
-#endif
-#define lstat mingw_lstat
-
-
-int mingw_utime(const char *file_name, const struct utimbuf *times);
-#define utime mingw_utime
-size_t mingw_strftime(char *s, size_t max,
- const char *format, const struct tm *tm);
-#define strftime mingw_strftime
-
-pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
- const char *dir,
- int fhin, int fhout, int fherr);
-int mingw_execvp(const char *cmd, char *const *argv);
-#define execvp mingw_execvp
-int mingw_execv(const char *cmd, char *const *argv);
-#define execv mingw_execv
-
-static inline unsigned int git_ntohl(unsigned int x)
-{ return (unsigned int)ntohl(x); }
-#define ntohl git_ntohl
-
-sig_handler_t mingw_signal(int sig, sig_handler_t handler);
-#define signal mingw_signal
-
-int mingw_raise(int sig);
-#define raise mingw_raise
-
-/*
- * ANSI emulation wrappers
- */
-
-int winansi_isatty(int fd);
-#define isatty winansi_isatty
-
-int winansi_dup2(int oldfd, int newfd);
-#define dup2 winansi_dup2
-
-void winansi_init(void);
-HANDLE winansi_get_osfhandle(int fd);
-
-/*
* git specific compatibility
*/
@@ -457,12 +39,6 @@ static inline void convert_slashes(char *path)
#define PATH_SEP ';'
char *mingw_query_user_email(void);
#define query_user_email mingw_query_user_email
-#if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
-#define PRIuMAX "I64u"
-#define PRId64 "I64d"
-#else
-#include <inttypes.h>
-#endif
/**
* Verifies that the specified path is owned by the user running the
diff --git a/compat/msvc-posix.h b/compat/msvc-posix.h
new file mode 100644
index 0000000000..c500b8b4aa
--- /dev/null
+++ b/compat/msvc-posix.h
@@ -0,0 +1,33 @@
+#ifndef COMPAT_MSVC_POSIX_H
+#define COMPAT_MSVC_POSIX_H
+
+#include <direct.h>
+#include <process.h>
+#include <malloc.h>
+#include <io.h>
+
+#pragma warning(disable: 4018) /* signed/unsigned comparison */
+#pragma warning(disable: 4244) /* type conversion, possible loss of data */
+#pragma warning(disable: 4090) /* 'function' : different 'const' qualifiers (ALLOC_GROW etc.)*/
+
+/* porting function */
+#define inline __inline
+#define __inline__ __inline
+#define __attribute__(x)
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#define ftruncate _chsize
+#define strtoull _strtoui64
+#define strtoll _strtoi64
+
+#undef ERROR
+
+#define ftello _ftelli64
+
+typedef int sigset_t;
+/* open for reading, writing, or both (not in fcntl.h) */
+#define O_ACCMODE (_O_RDONLY | _O_WRONLY | _O_RDWR)
+
+#include "mingw-posix.h"
+
+#endif /* COMPAT_MSVC_POSIX_H */
diff --git a/compat/msvc.h b/compat/msvc.h
index 1d7a8c6145..2b87c0a7c7 100644
--- a/compat/msvc.h
+++ b/compat/msvc.h
@@ -1,33 +1,7 @@
#ifndef __MSVC__HEAD
#define __MSVC__HEAD
-#include <direct.h>
-#include <process.h>
-#include <malloc.h>
-#include <io.h>
-
-#pragma warning(disable: 4018) /* signed/unsigned comparison */
-#pragma warning(disable: 4244) /* type conversion, possible loss of data */
-#pragma warning(disable: 4090) /* 'function' : different 'const' qualifiers (ALLOC_GROW etc.)*/
-
-/* porting function */
-#define inline __inline
-#define __inline__ __inline
-#define __attribute__(x)
-#define strcasecmp _stricmp
-#define strncasecmp _strnicmp
-#define ftruncate _chsize
-#define strtoull _strtoui64
-#define strtoll _strtoi64
-
-#undef ERROR
-
-#define ftello _ftelli64
-
-typedef int sigset_t;
-/* open for reading, writing, or both (not in fcntl.h) */
-#define O_ACCMODE (_O_RDONLY | _O_WRONLY | _O_RDWR)
-
-#include "compat/mingw.h"
+#include "msvc-posix.h"
+#include "mingw.h"
#endif
diff --git a/compat/posix.h b/compat/posix.h
new file mode 100644
index 0000000000..f4c71f9427
--- /dev/null
+++ b/compat/posix.h
@@ -0,0 +1,541 @@
+#ifndef COMPAT_POSIX_H
+#define COMPAT_POSIX_H
+
+#define _FILE_OFFSET_BITS 64
+
+/*
+ * Derived from Linux "Features Test Macro" header
+ * Convenience macros to test the versions of gcc (or
+ * a compatible compiler).
+ * Use them like this:
+ * #if GIT_GNUC_PREREQ (2,8)
+ * ... code requiring gcc 2.8 or later ...
+ * #endif
+ *
+ * This macro of course is not part of POSIX, but we need it for the UNUSED
+ * macro which is used by some of our POSIX compatibility wrappers.
+*/
+#if defined(__GNUC__) && defined(__GNUC_MINOR__)
+# define GIT_GNUC_PREREQ(maj, min) \
+ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+#else
+ #define GIT_GNUC_PREREQ(maj, min) 0
+#endif
+
+/*
+ * UNUSED marks a function parameter that is always unused. It also
+ * can be used to annotate a function, a variable, or a type that is
+ * always unused.
+ *
+ * A callback interface may dictate that a function accepts a
+ * parameter at that position, but the implementation of the function
+ * may not need to use the parameter. In such a case, mark the parameter
+ * with UNUSED.
+ *
+ * When a parameter may be used or unused, depending on conditional
+ * compilation, consider using MAYBE_UNUSED instead.
+ */
+#if GIT_GNUC_PREREQ(4, 5)
+#define UNUSED __attribute__((unused)) \
+ __attribute__((deprecated ("parameter declared as UNUSED")))
+#elif defined(__GNUC__)
+#define UNUSED __attribute__((unused)) \
+ __attribute__((deprecated))
+#else
+#define UNUSED
+#endif
+
+#ifdef __MINGW64__
+#define _POSIX_C_SOURCE 1
+#elif defined(__sun__)
+ /*
+ * On Solaris, when _XOPEN_EXTENDED is set, its header file
+ * forces the programs to be XPG4v2, defeating any _XOPEN_SOURCE
+ * setting to say we are XPG5 or XPG6. Also on Solaris,
+ * XPG6 programs must be compiled with a c99 compiler, while
+ * non XPG6 programs must be compiled with a pre-c99 compiler.
+ */
+# if __STDC_VERSION__ - 0 >= 199901L
+# define _XOPEN_SOURCE 600
+# else
+# define _XOPEN_SOURCE 500
+# endif
+#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
+ !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \
+ !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__) && \
+ !defined(__CYGWIN__)
+#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
+#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
+#endif
+#define _ALL_SOURCE 1
+#define _GNU_SOURCE 1
+#define _BSD_SOURCE 1
+#define _DEFAULT_SOURCE 1
+#define _NETBSD_SOURCE 1
+#define _SGI_SOURCE 1
+
+#if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
+# if !defined(_WIN32_WINNT)
+# define _WIN32_WINNT 0x0600
+# endif
+#define WIN32_LEAN_AND_MEAN /* stops windows.h including winsock.h */
+#include <winsock2.h>
+#ifndef NO_UNIX_SOCKETS
+#include <afunix.h>
+#endif
+#include <windows.h>
+#define GIT_WINDOWS_NATIVE
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h> /* for strcasecmp() */
+#endif
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#ifdef NEEDS_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <assert.h>
+#include <regex.h>
+#include <utime.h>
+#include <syslog.h>
+#if !defined(NO_POLL_H)
+#include <poll.h>
+#elif !defined(NO_SYS_POLL_H)
+#include <sys/poll.h>
+#else
+/* Pull the compat stuff */
+#include <poll.h>
+#endif
+#ifdef HAVE_BSD_SYSCTL
+#include <sys/sysctl.h>
+#endif
+
+#if defined(__MINGW32__)
+#include "mingw-posix.h"
+#elif defined(_MSC_VER)
+#include "msvc-posix.h"
+#else
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/statvfs.h>
+#include <termios.h>
+#ifndef NO_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <sys/un.h>
+#ifndef NO_INTTYPES_H
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+#ifdef HAVE_ARC4RANDOM_LIBBSD
+#include <bsd/stdlib.h>
+#endif
+#ifdef HAVE_GETRANDOM
+#include <sys/random.h>
+#endif
+#ifdef NO_INTPTR_T
+/*
+ * On I16LP32, ILP32 and LP64 "long" is the safe bet, however
+ * on LLP86, IL33LLP64 and P64 it needs to be "long long",
+ * while on IP16 and IP16L32 it is "int" (resp. "short")
+ * Size needs to match (or exceed) 'sizeof(void *)'.
+ * We can't take "long long" here as not everybody has it.
+ */
+typedef long intptr_t;
+typedef unsigned long uintptr_t;
+#endif
+#undef _ALL_SOURCE /* AIX 5.3L defines a struct list with _ALL_SOURCE. */
+#include <grp.h>
+#define _ALL_SOURCE 1
+#endif
+
+#ifdef MKDIR_WO_TRAILING_SLASH
+#define mkdir(a,b) compat_mkdir_wo_trailing_slash((a),(b))
+int compat_mkdir_wo_trailing_slash(const char*, mode_t);
+#endif
+
+#ifdef time
+#undef time
+#endif
+static inline time_t git_time(time_t *tloc)
+{
+ struct timeval tv;
+
+ /*
+ * Avoid time(NULL), which can disagree with gettimeofday(2)
+ * and filesystem timestamps.
+ */
+ gettimeofday(&tv, NULL);
+
+ if (tloc)
+ *tloc = tv.tv_sec;
+ return tv.tv_sec;
+}
+#define time git_time
+
+#ifdef NO_STRUCT_ITIMERVAL
+struct itimerval {
+ struct timeval it_interval;
+ struct timeval it_value;
+};
+#endif
+
+#ifdef NO_SETITIMER
+static inline int git_setitimer(int which UNUSED,
+ const struct itimerval *value UNUSED,
+ struct itimerval *newvalue UNUSED) {
+ return 0; /* pretend success */
+}
+#undef setitimer
+#define setitimer(which,value,ovalue) git_setitimer(which,value,ovalue)
+#endif
+
+#ifndef NO_LIBGEN_H
+#include <libgen.h>
+#else
+#define basename gitbasename
+char *gitbasename(char *);
+#define dirname gitdirname
+char *gitdirname(char *);
+#endif
+
+#ifndef NO_ICONV
+#include <iconv.h>
+#endif
+
+/* On most systems <netdb.h> would have given us this, but
+ * not on some systems (e.g. z/OS).
+ */
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 1025
+#endif
+
+#ifndef NI_MAXSERV
+#define NI_MAXSERV 32
+#endif
+
+/* On most systems <limits.h> would have given us this, but
+ * not on some systems (e.g. GNU/Hurd).
+ */
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#ifndef NAME_MAX
+#define NAME_MAX 255
+#endif
+
+typedef uintmax_t timestamp_t;
+#define PRItime PRIuMAX
+#define parse_timestamp strtoumax
+#define TIME_MAX UINTMAX_MAX
+#define TIME_MIN 0
+
+int lstat_cache_aware_rmdir(const char *path);
+#if !defined(__MINGW32__) && !defined(_MSC_VER)
+#define rmdir lstat_cache_aware_rmdir
+#endif
+
+#if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
+
+#ifndef PROT_READ
+#define PROT_READ 1
+#define PROT_WRITE 2
+#define MAP_PRIVATE 1
+#endif
+
+#define mmap git_mmap
+#define munmap git_munmap
+void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+int git_munmap(void *start, size_t length);
+
+#else /* NO_MMAP || USE_WIN32_MMAP */
+
+#include <sys/mman.h>
+
+#endif /* NO_MMAP || USE_WIN32_MMAP */
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *)-1)
+#endif
+
+#ifdef NEEDS_MODE_TRANSLATION
+#undef S_IFMT
+#undef S_IFREG
+#undef S_IFDIR
+#undef S_IFLNK
+#undef S_IFBLK
+#undef S_IFCHR
+#undef S_IFIFO
+#undef S_IFSOCK
+#define S_IFMT 0170000
+#define S_IFREG 0100000
+#define S_IFDIR 0040000
+#define S_IFLNK 0120000
+#define S_IFBLK 0060000
+#define S_IFCHR 0020000
+#define S_IFIFO 0010000
+#define S_IFSOCK 0140000
+#ifdef stat
+#undef stat
+#endif
+#define stat(path, buf) git_stat(path, buf)
+int git_stat(const char *, struct stat *);
+#ifdef fstat
+#undef fstat
+#endif
+#define fstat(fd, buf) git_fstat(fd, buf)
+int git_fstat(int, struct stat *);
+#ifdef lstat
+#undef lstat
+#endif
+#define lstat(path, buf) git_lstat(path, buf)
+int git_lstat(const char *, struct stat *);
+#endif
+
+#ifdef NO_PREAD
+#define pread git_pread
+ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
+#endif
+
+#ifdef NO_SETENV
+#define setenv gitsetenv
+int gitsetenv(const char *, const char *, int);
+#endif
+
+#ifdef NO_MKDTEMP
+#define mkdtemp gitmkdtemp
+char *gitmkdtemp(char *);
+#endif
+
+#ifdef NO_UNSETENV
+#define unsetenv gitunsetenv
+int gitunsetenv(const char *);
+#endif
+
+#ifdef NO_STRCASESTR
+#define strcasestr gitstrcasestr
+char *gitstrcasestr(const char *haystack, const char *needle);
+#endif
+
+#ifdef NO_STRLCPY
+#define strlcpy gitstrlcpy
+size_t gitstrlcpy(char *, const char *, size_t);
+#endif
+
+#ifdef NO_STRTOUMAX
+#define strtoumax gitstrtoumax
+uintmax_t gitstrtoumax(const char *, char **, int);
+#define strtoimax gitstrtoimax
+intmax_t gitstrtoimax(const char *, char **, int);
+#endif
+
+#ifdef NO_HSTRERROR
+#define hstrerror githstrerror
+const char *githstrerror(int herror);
+#endif
+
+#ifdef NO_MEMMEM
+#define memmem gitmemmem
+void *gitmemmem(const void *haystack, size_t haystacklen,
+ const void *needle, size_t needlelen);
+#endif
+
+#ifdef OVERRIDE_STRDUP
+#ifdef strdup
+#undef strdup
+#endif
+#define strdup gitstrdup
+char *gitstrdup(const char *s);
+#endif
+
+#ifdef NO_GETPAGESIZE
+#define getpagesize() sysconf(_SC_PAGESIZE)
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifdef FREAD_READS_DIRECTORIES
+# if !defined(SUPPRESS_FOPEN_REDEFINITION)
+# ifdef fopen
+# undef fopen
+# endif
+# define fopen(a,b) git_fopen(a,b)
+# endif
+FILE *git_fopen(const char*, const char*);
+#endif
+
+#ifdef SNPRINTF_RETURNS_BOGUS
+#ifdef snprintf
+#undef snprintf
+#endif
+#define snprintf git_snprintf
+int git_snprintf(char *str, size_t maxsize,
+ const char *format, ...);
+#ifdef vsnprintf
+#undef vsnprintf
+#endif
+#define vsnprintf git_vsnprintf
+int git_vsnprintf(char *str, size_t maxsize,
+ const char *format, va_list ap);
+#endif
+
+#ifdef OPEN_RETURNS_EINTR
+#undef open
+#define open git_open_with_retry
+int git_open_with_retry(const char *path, int flag, ...);
+#endif
+
+#ifdef __GLIBC_PREREQ
+#if __GLIBC_PREREQ(2, 1)
+#define HAVE_STRCHRNUL
+#endif
+#endif
+
+#ifndef HAVE_STRCHRNUL
+#define strchrnul gitstrchrnul
+static inline char *gitstrchrnul(const char *s, int c)
+{
+ while (*s && *s != c)
+ s++;
+ return (char *)s;
+}
+#endif
+
+#ifdef NO_INET_PTON
+int inet_pton(int af, const char *src, void *dst);
+#endif
+
+#ifdef NO_INET_NTOP
+const char *inet_ntop(int af, const void *src, char *dst, size_t size);
+#endif
+
+#ifdef NO_PTHREADS
+#define atexit git_atexit
+int git_atexit(void (*handler)(void));
+#endif
+
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 256
+#endif
+
+#include "../sane-ctype.h"
+
+void git_stable_qsort(void *base, size_t nmemb, size_t size,
+ int(*compar)(const void *, const void *));
+#ifdef INTERNAL_QSORT
+#define qsort git_stable_qsort
+#endif
+
+#define QSORT(base, n, compar) sane_qsort((base), (n), sizeof(*(base)), compar)
+static inline void sane_qsort(void *base, size_t nmemb, size_t size,
+ int(*compar)(const void *, const void *))
+{
+ if (nmemb > 1)
+ qsort(base, nmemb, size, compar);
+}
+
+#define STABLE_QSORT(base, n, compar) \
+ git_stable_qsort((base), (n), sizeof(*(base)), compar)
+
+#ifndef HAVE_ISO_QSORT_S
+int git_qsort_s(void *base, size_t nmemb, size_t size,
+ int (*compar)(const void *, const void *, void *), void *ctx);
+#define qsort_s git_qsort_s
+#endif
+
+#define QSORT_S(base, n, compar, ctx) do { \
+ if (qsort_s((base), (n), sizeof(*(base)), compar, ctx)) \
+ BUG("qsort_s() failed"); \
+} while (0)
+
+#ifdef NO_NSEC
+#undef USE_NSEC
+#define ST_CTIME_NSEC(st) 0
+#define ST_MTIME_NSEC(st) 0
+#else
+#ifdef USE_ST_TIMESPEC
+#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec))
+#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec))
+#else
+#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec))
+#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec))
+#endif
+#endif
+
+#ifndef va_copy
+/*
+ * Since an obvious implementation of va_list would be to make it a
+ * pointer into the stack frame, a simple assignment will work on
+ * many systems. But let's try to be more portable.
+ */
+#ifdef __va_copy
+#define va_copy(dst, src) __va_copy(dst, src)
+#else
+#define va_copy(dst, src) ((dst) = (src))
+#endif
+#endif
+
+#ifndef _POSIX_THREAD_SAFE_FUNCTIONS
+static inline void git_flockfile(FILE *fh UNUSED)
+{
+ ; /* nothing */
+}
+static inline void git_funlockfile(FILE *fh UNUSED)
+{
+ ; /* nothing */
+}
+#undef flockfile
+#undef funlockfile
+#undef getc_unlocked
+#define flockfile(fh) git_flockfile(fh)
+#define funlockfile(fh) git_funlockfile(fh)
+#define getc_unlocked(fh) getc(fh)
+#endif
+
+#ifdef FILENO_IS_A_MACRO
+int git_fileno(FILE *stream);
+# ifndef COMPAT_CODE_FILENO
+# undef fileno
+# define fileno(p) git_fileno(p)
+# endif
+#endif
+
+#ifdef NEED_ACCESS_ROOT_HANDLER
+int git_access(const char *path, int mode);
+# ifndef COMPAT_CODE_ACCESS
+# ifdef access
+# undef access
+# endif
+# define access(path, mode) git_access(path, mode)
+# endif
+#endif
+
+#endif /* COMPAT_POSIX_H */
diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c
index f7cc7b3be5..12e38e0ea3 100644
--- a/compat/precompose_utf8.c
+++ b/compat/precompose_utf8.c
@@ -50,15 +50,15 @@ void probe_utf8_pathname_composition(void)
int output_fd;
if (precomposed_unicode != -1)
return; /* We found it defined in the global config, respect it */
- git_path_buf(&path, "%s", auml_nfc);
+ repo_git_path_replace(the_repository, &path, "%s", auml_nfc);
output_fd = open(path.buf, O_CREAT|O_EXCL|O_RDWR, 0600);
if (output_fd >= 0) {
close(output_fd);
- git_path_buf(&path, "%s", auml_nfd);
+ repo_git_path_replace(the_repository, &path, "%s", auml_nfd);
precomposed_unicode = access(path.buf, R_OK) ? 0 : 1;
git_config_set("core.precomposeunicode",
precomposed_unicode ? "true" : "false");
- git_path_buf(&path, "%s", auml_nfc);
+ repo_git_path_replace(the_repository, &path, "%s", auml_nfc);
if (unlink(path.buf))
die_errno(_("failed to unlink '%s'"), path.buf);
}
diff --git a/compiler-tricks/not-constant.c b/compiler-tricks/not-constant.c
new file mode 100644
index 0000000000..1da3ffc2f5
--- /dev/null
+++ b/compiler-tricks/not-constant.c
@@ -0,0 +1,2 @@
+#include <git-compat-util.h>
+int false_but_the_compiler_does_not_know_it_;
diff --git a/config.c b/config.c
index 36f76fafe5..e127afaa8f 100644
--- a/config.c
+++ b/config.c
@@ -1437,11 +1437,6 @@ static int git_default_core_config(const char *var, const char *value,
return git_config_pathname(&git_attributes_file, var, value);
}
- if (!strcmp(var, "core.hookspath")) {
- FREE_AND_NULL(git_hooks_path);
- return git_config_pathname(&git_hooks_path, var, value);
- }
-
if (!strcmp(var, "core.bare")) {
is_bare_repository_cfg = git_config_bool(var, value);
return 0;
@@ -1652,7 +1647,7 @@ static int git_default_core_config(const char *var, const char *value,
return 0;
}
- /* Add other config variables here and to Documentation/config.txt. */
+ /* Add other config variables here and to Documentation/config.adoc. */
return platform_core_config(var, value, ctx, cb);
}
@@ -1663,7 +1658,7 @@ static int git_default_sparse_config(const char *var, const char *value)
return 0;
}
- /* Add other config variables here and to Documentation/config/sparse.txt. */
+ /* Add other config variables here and to Documentation/config/sparse.adoc. */
return 0;
}
@@ -1679,7 +1674,7 @@ static int git_default_i18n_config(const char *var, const char *value)
return git_config_string(&git_log_output_encoding, var, value);
}
- /* Add other config variables here and to Documentation/config.txt. */
+ /* Add other config variables here and to Documentation/config.adoc. */
return 0;
}
@@ -1715,7 +1710,7 @@ static int git_default_branch_config(const char *var, const char *value)
return 0;
}
- /* Add other config variables here and to Documentation/config.txt. */
+ /* Add other config variables here and to Documentation/config.adoc. */
return 0;
}
@@ -1744,7 +1739,7 @@ static int git_default_push_config(const char *var, const char *value)
return 0;
}
- /* Add other config variables here and to Documentation/config.txt. */
+ /* Add other config variables here and to Documentation/config.adoc. */
return 0;
}
@@ -1760,7 +1755,7 @@ static int git_default_mailmap_config(const char *var, const char *value)
return git_config_string(&git_mailmap_blob, var, value);
}
- /* Add other config variables here and to Documentation/config.txt. */
+ /* Add other config variables here and to Documentation/config.adoc. */
return 0;
}
@@ -1773,7 +1768,7 @@ static int git_default_attr_config(const char *var, const char *value)
/*
* Add other attribute related config variables here and to
- * Documentation/config/attr.txt.
+ * Documentation/config/attr.adoc.
*/
return 0;
}
@@ -1831,7 +1826,7 @@ int git_default_config(const char *var, const char *value,
if (starts_with(var, "sparse."))
return git_default_sparse_config(var, value);
- /* Add other config variables here and to Documentation/config.txt. */
+ /* Add other config variables here and to Documentation/config.adoc. */
return 0;
}
@@ -2526,6 +2521,10 @@ void repo_config_clear(struct repository *repo)
void repo_config(struct repository *repo, config_fn_t fn, void *data)
{
+ if (!repo) {
+ read_very_early_config(fn, data);
+ return;
+ }
git_config_check_init(repo);
configset_iter(repo->config, fn, data);
}
diff --git a/config.h b/config.h
index 5c730c4f89..29a0277483 100644
--- a/config.h
+++ b/config.h
@@ -219,6 +219,15 @@ void read_very_early_config(config_fn_t cb, void *data);
* repo-specific one; by overwriting, the higher-priority repo-specific
* value is left at the end).
*
+ * In cases where the repository variable is NULL, repo_config() will
+ * skip the per-repository config but retain system and global configs
+ * by calling read_very_early_config() which also ignores one-time
+ * overrides like "git -c var=val". This is to support handling "git foo -h"
+ * (which lets git.c:run_builtin() to pass NULL and have the cmd_foo()
+ * call repo_config() before calling parse_options() to notice "-h", give
+ * help and exit) for a command that ordinarily require a repository
+ * so this limitation may be OK (but if needed you are welcome to fix it).
+ *
* Unlike git_config_from_file(), this function respects includes.
*/
void repo_config(struct repository *r, config_fn_t fn, void *);
diff --git a/config.mak.dev b/config.mak.dev
index 0fd8cc4d35..95b7bc46ae 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -39,6 +39,7 @@ DEVELOPER_CFLAGS += -Wunused
DEVELOPER_CFLAGS += -Wvla
DEVELOPER_CFLAGS += -Wwrite-strings
DEVELOPER_CFLAGS += -fno-common
+DEVELOPER_CFLAGS += -Wunreachable-code
ifneq ($(filter clang4,$(COMPILER_FEATURES)),)
DEVELOPER_CFLAGS += -Wtautological-constant-out-of-range-compare
diff --git a/connect.c b/connect.c
index e6e25a0479..3280435331 100644
--- a/connect.c
+++ b/connect.c
@@ -22,6 +22,7 @@
#include "protocol.h"
#include "alias.h"
#include "bundle-uri.h"
+#include "promisor-remote.h"
static char *server_capabilities_v1;
static struct strvec server_capabilities_v2 = STRVEC_INIT;
@@ -487,6 +488,7 @@ void check_stateless_delimiter(int stateless_rpc,
static void send_capabilities(int fd_out, struct packet_reader *reader)
{
const char *hash_name;
+ const char *promisor_remote_info;
if (server_supports_v2("agent"))
packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
@@ -500,6 +502,13 @@ static void send_capabilities(int fd_out, struct packet_reader *reader)
} else {
reader->hash_algo = &hash_algos[GIT_HASH_SHA1];
}
+ if (server_feature_v2("promisor-remote", &promisor_remote_info)) {
+ char *reply = promisor_remote_reply(promisor_remote_info);
+ if (reply) {
+ packet_write_fmt(fd_out, "promisor-remote=%s", reply);
+ free(reply);
+ }
+ }
}
int get_remote_bundle_uri(int fd_out, struct packet_reader *reader,
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index c6fbd57e15..25b495fa73 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -1001,10 +1001,14 @@ parse_makefile_for_sources(unit-test_SOURCES ${CMAKE_SOURCE_DIR}/Makefile "UNIT_
list(TRANSFORM unit-test_SOURCES REPLACE "\\$\\(UNIT_TEST_DIR\\)/" "${CMAKE_SOURCE_DIR}/t/unit-tests/")
add_library(unit-test-lib STATIC ${unit-test_SOURCES})
+parse_makefile_for_sources(clar-test_SOURCES ${CMAKE_SOURCE_DIR}/Makefile "CLAR_TEST_OBJS")
+list(TRANSFORM clar-test_SOURCES REPLACE "\\$\\(UNIT_TEST_DIR\\)/" "${CMAKE_SOURCE_DIR}/t/unit-tests/")
+add_library(clar-test-lib STATIC ${clar-test_SOURCES})
+
parse_makefile_for_scripts(unit_test_PROGRAMS "UNIT_TEST_PROGRAMS" "")
foreach(unit_test ${unit_test_PROGRAMS})
add_executable("${unit_test}" "${CMAKE_SOURCE_DIR}/t/unit-tests/${unit_test}.c")
- target_link_libraries("${unit_test}" unit-test-lib common-main)
+ target_link_libraries("${unit_test}" unit-test-lib clar-test-lib common-main)
set_target_properties("${unit_test}"
PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/t/unit-tests/bin)
if(MSVC)
@@ -1046,13 +1050,13 @@ add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/t/unit-tests/clar.suite"
VERBATIM)
add_library(unit-tests-lib ${clar_test_SUITES}
- "${CMAKE_SOURCE_DIR}/t/unit-tests/clar/clar.c"
"${CMAKE_BINARY_DIR}/t/unit-tests/clar-decls.h"
"${CMAKE_BINARY_DIR}/t/unit-tests/clar.suite"
)
+target_include_directories(clar-test-lib PUBLIC "${CMAKE_BINARY_DIR}/t/unit-tests")
target_include_directories(unit-tests-lib PUBLIC "${CMAKE_BINARY_DIR}/t/unit-tests")
-add_executable(unit-tests "${CMAKE_SOURCE_DIR}/t/unit-tests/unit-test.c")
-target_link_libraries(unit-tests unit-tests-lib common-main)
+add_executable(unit-tests)
+target_link_libraries(unit-tests unit-tests-lib clar-test-lib common-main)
set_target_properties(unit-tests
PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/t/unit-tests/bin)
if(MSVC)
diff --git a/contrib/coccinelle/meson.build b/contrib/coccinelle/meson.build
index 5d76a7fee6..ea054c924f 100644
--- a/contrib/coccinelle/meson.build
+++ b/contrib/coccinelle/meson.build
@@ -1,4 +1,9 @@
-spatch = find_program('spatch', required: get_option('coccinelle'))
+coccinelle_opt = get_option('coccinelle').require(
+ fs.exists(meson.project_source_root() / '.git'),
+ error_message: 'coccinelle can only be run from a git checkout',
+)
+
+spatch = find_program('spatch', required: coccinelle_opt)
if not spatch.found()
subdir_done()
endif
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 413911be3b..e3d88b0672 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -234,6 +234,17 @@ __git_dequote ()
done
}
+# Prints the number of slash-separated components in a path.
+# 1: Path to count components of.
+__git_count_path_components ()
+{
+ local path="$1"
+ local relative="${path#/}"
+ relative="${relative%/}"
+ local slashes="/${relative//[^\/]}"
+ echo "${#slashes}"
+}
+
# The following function is based on code from:
#
# bash_completion - programmable completion functions for bash 3.2+
@@ -779,16 +790,39 @@ __git_tags ()
__git_dwim_remote_heads ()
{
local pfx="${1-}" cur_="${2-}" sfx="${3-}"
- local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers
# employ the heuristic used by git checkout and git switch
# Try to find a remote branch that cur_es the completion word
# but only output if the branch name is unique
- __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
- --sort="refname:strip=3" \
- ${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
- "refs/remotes/*/$cur_*" "refs/remotes/*/$cur_*/**" | \
- uniq -u
+ local awk_script='
+ function casemap(s) {
+ if (ENVIRON["IGNORE_CASE"])
+ return tolower(s)
+ else
+ return s
+ }
+ BEGIN {
+ split(ENVIRON["REMOTES"], remotes, /\n/)
+ for (i in remotes)
+ remotes[i] = "refs/remotes/" casemap(remotes[i])
+ cur_ = casemap(ENVIRON["CUR_"])
+ }
+ {
+ ref_case = casemap($0)
+ for (i in remotes) {
+ if (index(ref_case, remotes[i] "/" cur_) == 1) {
+ branch = substr($0, length(remotes[i] "/") + 1)
+ print ENVIRON["PFX"] branch ENVIRON["SFX"]
+ break
+ }
+ }
+ }
+ '
+ __git for-each-ref --format='%(refname)' refs/remotes/ |
+ PFX="$pfx" SFX="$sfx" CUR_="$cur_" \
+ IGNORE_CASE=${GIT_COMPLETION_IGNORE_CASE+1} \
+ REMOTES="$(__git_remotes | sort -r)" awk "$awk_script" |
+ sort | uniq -u
}
# Lists refs from the local (by default) or from a remote repository.
@@ -894,7 +928,8 @@ __git_refs ()
case "HEAD" in
$match*|$umatch*) echo "${pfx}HEAD$sfx" ;;
esac
- __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
+ local strip="$(__git_count_path_components "refs/remotes/$remote")"
+ __git for-each-ref --format="$fer_pfx%(refname:strip=$strip)$sfx" \
${GIT_COMPLETION_IGNORE_CASE+--ignore-case} \
"refs/remotes/$remote/$match*" \
"refs/remotes/$remote/$match*/**"
diff --git a/contrib/contacts/Makefile b/contrib/contacts/Makefile
index a2990f0dcb..9c4ca4f3bc 100644
--- a/contrib/contacts/Makefile
+++ b/contrib/contacts/Makefile
@@ -34,7 +34,7 @@ GIT_CONTACTS := git-contacts
GIT_CONTACTS_DOC := git-contacts.1
GIT_CONTACTS_XML := git-contacts.xml
-GIT_CONTACTS_TXT := git-contacts.txt
+GIT_CONTACTS_TXT := git-contacts.adoc
GIT_CONTACTS_HTML := git-contacts.html
doc: $(GIT_CONTACTS_DOC) $(GIT_CONTACTS_HTML)
diff --git a/contrib/contacts/git-contacts.txt b/contrib/contacts/git-contacts.adoc
index dd914d1261..dd914d1261 100644
--- a/contrib/contacts/git-contacts.txt
+++ b/contrib/contacts/git-contacts.adoc
diff --git a/contrib/contacts/meson.build b/contrib/contacts/meson.build
index 6ec92f47c4..73d82dfe52 100644
--- a/contrib/contacts/meson.build
+++ b/contrib/contacts/meson.build
@@ -16,7 +16,7 @@ if get_option('docs').contains('man')
'@INPUT@',
],
depends: documentation_deps,
- input: 'git-contacts.txt',
+ input: 'git-contacts.adoc',
output: 'git-contacts.xml',
)
@@ -47,7 +47,7 @@ if get_option('docs').contains('html')
'@INPUT@',
],
depends: documentation_deps,
- input: 'git-contacts.txt',
+ input: 'git-contacts.adoc',
output: 'git-contacts.html',
install: true,
install_dir: get_option('datadir') / 'doc/git-doc',
diff --git a/contrib/long-running-filter/example.pl b/contrib/long-running-filter/example.pl
index a677569ddd..4b83e4c5e8 100755
--- a/contrib/long-running-filter/example.pl
+++ b/contrib/long-running-filter/example.pl
@@ -1,7 +1,7 @@
#!/usr/bin/perl
#
# Example implementation for the Git filter protocol version 2
-# See Documentation/gitattributes.txt, section "Filter Protocol"
+# See Documentation/gitattributes.adoc, section "Filter Protocol"
#
# Please note, this pass-thru filter is a minimal skeleton. No proper
# error handling was implemented.
diff --git a/contrib/subtree/Makefile b/contrib/subtree/Makefile
index 8fe0bfd401..c0c9f21cb7 100644
--- a/contrib/subtree/Makefile
+++ b/contrib/subtree/Makefile
@@ -50,7 +50,7 @@ GIT_SUBTREE := git-subtree
GIT_SUBTREE_DOC := git-subtree.1
GIT_SUBTREE_XML := git-subtree.xml
-GIT_SUBTREE_TXT := git-subtree.txt
+GIT_SUBTREE_TXT := git-subtree.adoc
GIT_SUBTREE_HTML := git-subtree.html
GIT_SUBTREE_TEST := ../../git-subtree
diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.adoc
index 004abf415b..004abf415b 100644
--- a/contrib/subtree/git-subtree.txt
+++ b/contrib/subtree/git-subtree.adoc
diff --git a/contrib/subtree/meson.build b/contrib/subtree/meson.build
index a752a188df..9c72b23625 100644
--- a/contrib/subtree/meson.build
+++ b/contrib/subtree/meson.build
@@ -32,7 +32,7 @@ if get_option('docs').contains('man')
'@INPUT@',
],
depends: documentation_deps,
- input: 'git-subtree.txt',
+ input: 'git-subtree.adoc',
output: 'git-subtree.xml',
)
@@ -63,7 +63,7 @@ if get_option('docs').contains('html')
'@INPUT@',
],
depends: documentation_deps,
- input: 'git-subtree.txt',
+ input: 'git-subtree.adoc',
output: 'git-subtree.html',
install: true,
install_dir: get_option('datadir') / 'doc/git-doc',
diff --git a/copy.c b/copy.c
index d9d2092012..b668209b6c 100644
--- a/copy.c
+++ b/copy.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "copy.h"
#include "path.h"
@@ -57,7 +59,7 @@ int copy_file(const char *dst, const char *src, int mode)
if (close(fdo) != 0)
return error_errno("%s: close error", dst);
- if (!status && adjust_shared_perm(dst))
+ if (!status && adjust_shared_perm(the_repository, dst))
return -1;
return status;
diff --git a/decorate.c b/decorate.c
index e161e13772..9f24925263 100644
--- a/decorate.c
+++ b/decorate.c
@@ -3,8 +3,6 @@
* data.
*/
-#define DISABLE_SIGN_COMPARE_WARNINGS
-
#include "git-compat-util.h"
#include "object.h"
#include "decorate.h"
@@ -16,9 +14,8 @@ static unsigned int hash_obj(const struct object *obj, unsigned int n)
static void *insert_decoration(struct decoration *n, const struct object *base, void *decoration)
{
- int size = n->size;
struct decoration_entry *entries = n->entries;
- unsigned int j = hash_obj(base, size);
+ unsigned int j = hash_obj(base, n->size);
while (entries[j].base) {
if (entries[j].base == base) {
@@ -26,7 +23,7 @@ static void *insert_decoration(struct decoration *n, const struct object *base,
entries[j].decoration = decoration;
return old;
}
- if (++j >= size)
+ if (++j >= n->size)
j = 0;
}
entries[j].base = base;
@@ -37,8 +34,8 @@ static void *insert_decoration(struct decoration *n, const struct object *base,
static void grow_decoration(struct decoration *n)
{
- int i;
- int old_size = n->size;
+ unsigned int i;
+ unsigned int old_size = n->size;
struct decoration_entry *old_entries = n->entries;
n->size = (old_size + 1000) * 3 / 2;
@@ -59,9 +56,7 @@ static void grow_decoration(struct decoration *n)
void *add_decoration(struct decoration *n, const struct object *obj,
void *decoration)
{
- int nr = n->nr + 1;
-
- if (nr > n->size * 2 / 3)
+ if ((n->nr + 1) > n->size * 2 / 3)
grow_decoration(n);
return insert_decoration(n, obj, decoration);
}
diff --git a/diff.c b/diff.c
index c89c15d98e..08f5e00a2c 100644
--- a/diff.c
+++ b/diff.c
@@ -7085,7 +7085,7 @@ void diffcore_std(struct diff_options *options)
diffcore_order(options->orderfile);
if (options->rotate_to)
diffcore_rotate(options);
- if (!options->found_follow)
+ if (!options->found_follow && !options->skip_resolving_statuses)
/* See try_to_follow_renames() in tree-diff.c */
diff_resolve_rename_copy();
diffcore_apply_filter(options);
@@ -7161,16 +7161,19 @@ void compute_diffstat(struct diff_options *options,
options->found_changes = !!diffstat->nr;
}
-void diff_addremove(struct diff_options *options,
- int addremove, unsigned mode,
- const struct object_id *oid,
- int oid_valid,
- const char *concatpath, unsigned dirty_submodule)
+struct diff_filepair *diff_queue_addremove(struct diff_queue_struct *queue,
+ struct diff_options *options,
+ int addremove, unsigned mode,
+ const struct object_id *oid,
+ int oid_valid,
+ const char *concatpath,
+ unsigned dirty_submodule)
{
struct diff_filespec *one, *two;
+ struct diff_filepair *pair;
if (S_ISGITLINK(mode) && is_submodule_ignored(concatpath, options))
- return;
+ return NULL;
/* This may look odd, but it is a preparation for
* feeding "there are unchanged files which should
@@ -7190,7 +7193,7 @@ void diff_addremove(struct diff_options *options,
if (options->prefix &&
strncmp(concatpath, options->prefix, options->prefix_length))
- return;
+ return NULL;
one = alloc_filespec(concatpath);
two = alloc_filespec(concatpath);
@@ -7202,25 +7205,29 @@ void diff_addremove(struct diff_options *options,
two->dirty_submodule = dirty_submodule;
}
- diff_queue(&diff_queued_diff, one, two);
+ pair = diff_queue(queue, one, two);
if (!options->flags.diff_from_contents)
options->flags.has_changes = 1;
+
+ return pair;
}
-void diff_change(struct diff_options *options,
- unsigned old_mode, unsigned new_mode,
- const struct object_id *old_oid,
- const struct object_id *new_oid,
- int old_oid_valid, int new_oid_valid,
- const char *concatpath,
- unsigned old_dirty_submodule, unsigned new_dirty_submodule)
+struct diff_filepair *diff_queue_change(struct diff_queue_struct *queue,
+ struct diff_options *options,
+ unsigned old_mode, unsigned new_mode,
+ const struct object_id *old_oid,
+ const struct object_id *new_oid,
+ int old_oid_valid, int new_oid_valid,
+ const char *concatpath,
+ unsigned old_dirty_submodule,
+ unsigned new_dirty_submodule)
{
struct diff_filespec *one, *two;
struct diff_filepair *p;
if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) &&
is_submodule_ignored(concatpath, options))
- return;
+ return NULL;
if (options->flags.reverse_diff) {
SWAP(old_mode, new_mode);
@@ -7231,7 +7238,7 @@ void diff_change(struct diff_options *options,
if (options->prefix &&
strncmp(concatpath, options->prefix, options->prefix_length))
- return;
+ return NULL;
one = alloc_filespec(concatpath);
two = alloc_filespec(concatpath);
@@ -7239,19 +7246,42 @@ void diff_change(struct diff_options *options,
fill_filespec(two, new_oid, new_oid_valid, new_mode);
one->dirty_submodule = old_dirty_submodule;
two->dirty_submodule = new_dirty_submodule;
- p = diff_queue(&diff_queued_diff, one, two);
+ p = diff_queue(queue, one, two);
if (options->flags.diff_from_contents)
- return;
+ return p;
if (options->flags.quick && options->skip_stat_unmatch &&
!diff_filespec_check_stat_unmatch(options->repo, p)) {
diff_free_filespec_data(p->one);
diff_free_filespec_data(p->two);
- return;
+ return p;
}
options->flags.has_changes = 1;
+
+ return p;
+}
+
+void diff_addremove(struct diff_options *options, int addremove, unsigned mode,
+ const struct object_id *oid, int oid_valid,
+ const char *concatpath, unsigned dirty_submodule)
+{
+ diff_queue_addremove(&diff_queued_diff, options, addremove, mode, oid,
+ oid_valid, concatpath, dirty_submodule);
+}
+
+void diff_change(struct diff_options *options,
+ unsigned old_mode, unsigned new_mode,
+ const struct object_id *old_oid,
+ const struct object_id *new_oid,
+ int old_oid_valid, int new_oid_valid,
+ const char *concatpath,
+ unsigned old_dirty_submodule, unsigned new_dirty_submodule)
+{
+ diff_queue_change(&diff_queued_diff, options, old_mode, new_mode,
+ old_oid, new_oid, old_oid_valid, new_oid_valid,
+ concatpath, old_dirty_submodule, new_dirty_submodule);
}
struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path)
diff --git a/diff.h b/diff.h
index 0a566f5531..42463edbdd 100644
--- a/diff.h
+++ b/diff.h
@@ -333,7 +333,7 @@ struct diff_options {
int xdl_opts;
int ignore_driver_algorithm;
- /* see Documentation/diff-options.txt */
+ /* see Documentation/diff-options.adoc */
char **anchors;
size_t anchors_nr, anchors_alloc;
@@ -353,6 +353,14 @@ struct diff_options {
/* to support internal diff recursion by --follow hack*/
int found_follow;
+ /*
+ * By default, diffcore_std() resolves the statuses for queued diff file
+ * pairs by calling diff_resolve_rename_copy(). If status information
+ * has already been manually set, this option prevents diffcore_std()
+ * from resetting statuses.
+ */
+ int skip_resolving_statuses;
+
/* Callback which allows tweaking the options in diff_setup_done(). */
void (*set_default)(struct diff_options *);
@@ -508,6 +516,31 @@ void diff_set_default_prefix(struct diff_options *options);
int diff_can_quit_early(struct diff_options *);
+/*
+ * Stages changes in the provided diff queue for file additions and deletions.
+ * If a file pair gets queued, it is returned.
+ */
+struct diff_filepair *diff_queue_addremove(struct diff_queue_struct *queue,
+ struct diff_options *,
+ int addremove, unsigned mode,
+ const struct object_id *oid,
+ int oid_valid, const char *fullpath,
+ unsigned dirty_submodule);
+
+/*
+ * Stages changes in the provided diff queue for file modifications.
+ * If a file pair gets queued, it is returned.
+ */
+struct diff_filepair *diff_queue_change(struct diff_queue_struct *queue,
+ struct diff_options *,
+ unsigned mode1, unsigned mode2,
+ const struct object_id *old_oid,
+ const struct object_id *new_oid,
+ int old_oid_valid, int new_oid_valid,
+ const char *fullpath,
+ unsigned dirty_submodule1,
+ unsigned dirty_submodule2);
+
void diff_addremove(struct diff_options *,
int addremove,
unsigned mode,
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 91b77993c7..8077283fc7 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -33,7 +33,7 @@ static struct diff_rename_dst *locate_rename_dst(struct diff_filepair *p)
{
/* Lookup by p->ONE->path */
int idx = break_idx ? strintmap_get(break_idx, p->one->path) : -1;
- return (idx == -1) ? NULL : &rename_dst[idx];
+ return (idx == -1 || idx == rename_dst_nr) ? NULL : &rename_dst[idx];
}
/*
@@ -1406,7 +1406,7 @@ void diffcore_rename_extended(struct diff_options *options,
trace2_region_enter("diff", "setup", options->repo);
info.setup = 0;
- assert(!dir_rename_count || strmap_empty(dir_rename_count));
+ ASSERT(!dir_rename_count || strmap_empty(dir_rename_count));
want_copies = (detect_rename == DIFF_DETECT_COPY);
if (dirs_removed && (break_idx || want_copies))
BUG("dirs_removed incompatible with break/copy detection");
@@ -1669,9 +1669,10 @@ void diffcore_rename_extended(struct diff_options *options,
if (DIFF_PAIR_BROKEN(p)) {
/* broken delete */
struct diff_rename_dst *dst = locate_rename_dst(p);
- if (!dst)
- BUG("tracking failed somehow; failed to find associated dst for broken pair");
- if (dst->is_rename)
+ if (options->single_follow && dst &&
+ strcmp(dst->p->two->path, p->two->path))
+ dst = NULL;
+ if (dst && dst->is_rename)
/* counterpart is now rename/copy */
pair_to_free = p;
}
diff --git a/diffcore.h b/diffcore.h
index 2feb325031..9c0a0e7aaf 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -107,7 +107,7 @@ struct diff_filepair {
struct diff_filespec *one;
struct diff_filespec *two;
unsigned short int score;
- char status; /* M C R A D U etc. (see Documentation/diff-format.txt or DIFF_STATUS_* in diff.h) */
+ char status; /* M C R A D U etc. (see Documentation/diff-format.adoc or DIFF_STATUS_* in diff.h) */
unsigned broken_pair : 1;
unsigned renamed_pair : 1;
unsigned is_unmerged : 1;
diff --git a/dir-iterator.c b/dir-iterator.c
index de619846f2..857e1d9bda 100644
--- a/dir-iterator.c
+++ b/dir-iterator.c
@@ -193,9 +193,9 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
if (S_ISDIR(iter->base.st.st_mode) && push_level(iter)) {
if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
- goto error_out;
+ return ITER_ERROR;
if (iter->levels_nr == 0)
- goto error_out;
+ return ITER_ERROR;
}
/* Loop until we find an entry that we can give back to the caller. */
@@ -211,11 +211,11 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
int ret = next_directory_entry(level->dir, iter->base.path.buf, &de);
if (ret < 0) {
if (iter->flags & DIR_ITERATOR_PEDANTIC)
- goto error_out;
+ return ITER_ERROR;
continue;
} else if (ret > 0) {
if (pop_level(iter) == 0)
- return dir_iterator_abort(dir_iterator);
+ return ITER_DONE;
continue;
}
@@ -223,7 +223,7 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
} else {
if (level->entries_idx >= level->entries.nr) {
if (pop_level(iter) == 0)
- return dir_iterator_abort(dir_iterator);
+ return ITER_DONE;
continue;
}
@@ -232,22 +232,21 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator)
if (prepare_next_entry_data(iter, name)) {
if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC)
- goto error_out;
+ return ITER_ERROR;
continue;
}
return ITER_OK;
}
-
-error_out:
- dir_iterator_abort(dir_iterator);
- return ITER_ERROR;
}
-int dir_iterator_abort(struct dir_iterator *dir_iterator)
+void dir_iterator_free(struct dir_iterator *dir_iterator)
{
struct dir_iterator_int *iter = (struct dir_iterator_int *)dir_iterator;
+ if (!iter)
+ return;
+
for (; iter->levels_nr; iter->levels_nr--) {
struct dir_iterator_level *level =
&iter->levels[iter->levels_nr - 1];
@@ -266,7 +265,6 @@ int dir_iterator_abort(struct dir_iterator *dir_iterator)
free(iter->levels);
strbuf_release(&iter->base.path);
free(iter);
- return ITER_DONE;
}
struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags)
@@ -301,7 +299,7 @@ struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags)
return dir_iterator;
error_out:
- dir_iterator_abort(dir_iterator);
+ dir_iterator_free(dir_iterator);
errno = saved_errno;
return NULL;
}
diff --git a/dir-iterator.h b/dir-iterator.h
index 6d438809b6..ccd6a19734 100644
--- a/dir-iterator.h
+++ b/dir-iterator.h
@@ -28,7 +28,7 @@
*
* while ((ok = dir_iterator_advance(iter)) == ITER_OK) {
* if (want_to_stop_iteration()) {
- * ok = dir_iterator_abort(iter);
+ * ok = ITER_DONE;
* break;
* }
*
@@ -39,6 +39,7 @@
*
* if (ok != ITER_DONE)
* handle_error();
+ * dir_iterator_free(iter);
*
* Callers are allowed to modify iter->path while they are working,
* but they must restore it to its original contents before calling
@@ -107,11 +108,7 @@ struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags);
*/
int dir_iterator_advance(struct dir_iterator *iterator);
-/*
- * End the iteration before it has been exhausted. Free the
- * dir_iterator and any associated resources and return ITER_DONE. On
- * error, free the dir_iterator and return ITER_ERROR.
- */
-int dir_iterator_abort(struct dir_iterator *iterator);
+/* Free the dir_iterator and any associated resources. */
+void dir_iterator_free(struct dir_iterator *iterator);
#endif
diff --git a/dir.c b/dir.c
index 16ccfe7e4e..cbd82be6c9 100644
--- a/dir.c
+++ b/dir.c
@@ -3451,7 +3451,7 @@ void setup_standard_excludes(struct dir_struct *dir)
char *get_sparse_checkout_filename(void)
{
- return git_pathdup("info/sparse-checkout");
+ return repo_git_path(the_repository, "info/sparse-checkout");
}
int get_sparse_checkout_patterns(struct pattern_list *pl)
diff --git a/dir.h b/dir.h
index 6cfef5df66..d7e71aa8da 100644
--- a/dir.h
+++ b/dir.h
@@ -43,7 +43,6 @@ struct repository;
*
*/
-struct repository;
struct dir_entry {
unsigned int len;
diff --git a/editor.c b/editor.c
index 6b9ce81d5f..b79d97b0e7 100644
--- a/editor.c
+++ b/editor.c
@@ -142,10 +142,8 @@ int strbuf_edit_interactively(struct repository *r,
struct strbuf sb = STRBUF_INIT;
int fd, res = 0;
- if (!is_absolute_path(path)) {
- strbuf_repo_git_path(&sb, r, "%s", path);
- path = sb.buf;
- }
+ if (!is_absolute_path(path))
+ path = repo_git_path_append(r, &sb, "%s", path);
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd < 0)
diff --git a/environment.c b/environment.c
index e5b361bb5d..9e4c7781be 100644
--- a/environment.c
+++ b/environment.c
@@ -43,7 +43,6 @@ char *git_log_output_encoding;
char *apply_default_whitespace;
char *apply_default_ignorewhitespace;
char *git_attributes_file;
-char *git_hooks_path;
int zlib_compression_level = Z_BEST_SPEED;
int pack_compression_level = Z_DEFAULT_COMPRESSION;
int fsync_object_files = -1;
@@ -208,32 +207,6 @@ const char *get_commit_output_encoding(void)
return git_commit_encoding ? git_commit_encoding : "UTF-8";
}
-static int the_shared_repository = PERM_UMASK;
-static int need_shared_repository_from_config = 1;
-
-void set_shared_repository(int value)
-{
- the_shared_repository = value;
- need_shared_repository_from_config = 0;
-}
-
-int get_shared_repository(void)
-{
- if (need_shared_repository_from_config) {
- const char *var = "core.sharedrepository";
- const char *value;
- if (!git_config_get_value(var, &value))
- the_shared_repository = git_config_perm(var, value);
- need_shared_repository_from_config = 0;
- }
- return the_shared_repository;
-}
-
-void reset_shared_repository(void)
-{
- need_shared_repository_from_config = 1;
-}
-
int use_optional_locks(void)
{
return git_env_bool(GIT_OPTIONAL_LOCKS_ENVIRONMENT, 1);
diff --git a/environment.h b/environment.h
index 2f43340f0b..45e690f203 100644
--- a/environment.h
+++ b/environment.h
@@ -134,16 +134,6 @@ void setup_git_env(const char *git_dir);
*/
int have_git_dir(void);
-/*
- * Accessors for the core.sharedrepository config which lazy-load the value
- * from the config (if not already set). The "reset" function can be
- * used to unset "set" or cached value, meaning that the value will be loaded
- * fresh from the config file on the next call to get_shared_repository().
- */
-void set_shared_repository(int value);
-int get_shared_repository(void);
-void reset_shared_repository(void);
-
extern int is_bare_repository_cfg;
int is_bare_repository(void);
extern char *git_work_tree_cfg;
@@ -160,7 +150,6 @@ extern int warn_on_object_refname_ambiguity;
extern char *apply_default_whitespace;
extern char *apply_default_ignorewhitespace;
extern char *git_attributes_file;
-extern char *git_hooks_path;
extern int zlib_compression_level;
extern int pack_compression_level;
extern size_t packed_git_window_size;
diff --git a/ewah/ewah_bitmap.c b/ewah/ewah_bitmap.c
index 67f8f588e0..056c410efb 100644
--- a/ewah/ewah_bitmap.c
+++ b/ewah/ewah_bitmap.c
@@ -371,6 +371,39 @@ void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent)
read_new_rlw(it);
}
+void ewah_or_iterator_init(struct ewah_or_iterator *it,
+ struct ewah_bitmap **parents, size_t nr)
+{
+ size_t i;
+
+ memset(it, 0, sizeof(*it));
+
+ ALLOC_ARRAY(it->its, nr);
+ for (i = 0; i < nr; i++)
+ ewah_iterator_init(&it->its[it->nr++], parents[i]);
+}
+
+int ewah_or_iterator_next(eword_t *next, struct ewah_or_iterator *it)
+{
+ eword_t buf, out = 0;
+ size_t i;
+ int ret = 0;
+
+ for (i = 0; i < it->nr; i++)
+ if (ewah_iterator_next(&buf, &it->its[i])) {
+ out |= buf;
+ ret = 1;
+ }
+
+ *next = out;
+ return ret;
+}
+
+void ewah_or_iterator_release(struct ewah_or_iterator *it)
+{
+ free(it->its);
+}
+
void ewah_xor(
struct ewah_bitmap *ewah_i,
struct ewah_bitmap *ewah_j,
diff --git a/ewah/ewok.h b/ewah/ewok.h
index 5e357e2493..c29d354236 100644
--- a/ewah/ewok.h
+++ b/ewah/ewok.h
@@ -148,6 +148,18 @@ void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent);
*/
int ewah_iterator_next(eword_t *next, struct ewah_iterator *it);
+struct ewah_or_iterator {
+ struct ewah_iterator *its;
+ size_t nr;
+};
+
+void ewah_or_iterator_init(struct ewah_or_iterator *it,
+ struct ewah_bitmap **parents, size_t nr);
+
+int ewah_or_iterator_next(eword_t *next, struct ewah_or_iterator *it);
+
+void ewah_or_iterator_release(struct ewah_or_iterator *it);
+
void ewah_xor(
struct ewah_bitmap *ewah_i,
struct ewah_bitmap *ewah_j,
diff --git a/fsck.h b/fsck.h
index a44c231a5f..b1deae61ee 100644
--- a/fsck.h
+++ b/fsck.h
@@ -15,7 +15,7 @@ enum fsck_msg_type {
};
/*
- * Documentation/fsck-msgids.txt documents these; when
+ * Documentation/fsck-msgids.adoc documents these; when
* modifying this list in any way, make sure to keep the
* two in sync.
*/
@@ -30,6 +30,8 @@ enum fsck_msg_type {
FUNC(BAD_EMAIL, ERROR) \
FUNC(BAD_NAME, ERROR) \
FUNC(BAD_OBJECT_SHA1, ERROR) \
+ FUNC(BAD_PACKED_REF_ENTRY, ERROR) \
+ FUNC(BAD_PACKED_REF_HEADER, ERROR) \
FUNC(BAD_PARENT_SHA1, ERROR) \
FUNC(BAD_REF_CONTENT, ERROR) \
FUNC(BAD_REF_FILETYPE, ERROR) \
@@ -53,6 +55,8 @@ enum fsck_msg_type {
FUNC(MISSING_TYPE, ERROR) \
FUNC(MISSING_TYPE_ENTRY, ERROR) \
FUNC(MULTIPLE_AUTHORS, ERROR) \
+ FUNC(PACKED_REF_ENTRY_NOT_TERMINATED, ERROR) \
+ FUNC(PACKED_REF_UNSORTED, ERROR) \
FUNC(TREE_NOT_SORTED, ERROR) \
FUNC(UNKNOWN_TYPE, ERROR) \
FUNC(ZERO_PADDED_DATE, ERROR) \
diff --git a/generate-configlist.sh b/generate-configlist.sh
index dffdaada8b..b06da53c89 100755
--- a/generate-configlist.sh
+++ b/generate-configlist.sh
@@ -13,10 +13,18 @@ print_config_list () {
cat <<EOF
static const char *config_name_list[] = {
EOF
- grep -h '^[a-zA-Z].*\..*::$' "$SOURCE_DIR"/Documentation/*config.adoc "$SOURCE_DIR"/Documentation/config/*.adoc |
- sed '/deprecated/d; s/::$//; s/, */\n/g' |
- sort |
- sed 's/^.*$/ "&",/'
+ sed -E '
+/^`?[a-zA-Z].*\..*`?::$/ {
+ /deprecated/d;
+ s/::$//;
+ s/`//g;
+ s/^.*$/ "&",/;
+ s/, */",\n "/g;
+ p;};
+d' \
+ "$SOURCE_DIR"/Documentation/*config.adoc \
+ "$SOURCE_DIR"/Documentation/config/*.adoc|
+ sort
cat <<EOF
NULL,
};
diff --git a/git-compat-util.h b/git-compat-util.h
index e123288e8f..afa040086f 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -23,26 +23,9 @@
#include <crtdbg.h>
#endif
-struct strbuf;
-
-
-#define _FILE_OFFSET_BITS 64
+#include "compat/posix.h"
-
-/* Derived from Linux "Features Test Macro" header
- * Convenience macros to test the versions of gcc (or
- * a compatible compiler).
- * Use them like this:
- * #if GIT_GNUC_PREREQ (2,8)
- * ... code requiring gcc 2.8 or later ...
- * #endif
-*/
-#if defined(__GNUC__) && defined(__GNUC_MINOR__)
-# define GIT_GNUC_PREREQ(maj, min) \
- ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
-#else
- #define GIT_GNUC_PREREQ(maj, min) 0
-#endif
+struct strbuf;
#if defined(__GNUC__) || defined(__clang__)
# define PRAGMA(pragma) _Pragma(#pragma)
@@ -176,71 +159,6 @@ DISABLE_WARNING(-Wsign-compare)
/* Approximation of the length of the decimal representation of this type. */
#define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1)
-#ifdef __MINGW64__
-#define _POSIX_C_SOURCE 1
-#elif defined(__sun__)
- /*
- * On Solaris, when _XOPEN_EXTENDED is set, its header file
- * forces the programs to be XPG4v2, defeating any _XOPEN_SOURCE
- * setting to say we are XPG5 or XPG6. Also on Solaris,
- * XPG6 programs must be compiled with a c99 compiler, while
- * non XPG6 programs must be compiled with a pre-c99 compiler.
- */
-# if __STDC_VERSION__ - 0 >= 199901L
-# define _XOPEN_SOURCE 600
-# else
-# define _XOPEN_SOURCE 500
-# endif
-#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
- !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \
- !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__) && \
- !defined(__CYGWIN__)
-#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
-#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
-#endif
-#define _ALL_SOURCE 1
-#define _GNU_SOURCE 1
-#define _BSD_SOURCE 1
-#define _DEFAULT_SOURCE 1
-#define _NETBSD_SOURCE 1
-#define _SGI_SOURCE 1
-
-/*
- * UNUSED marks a function parameter that is always unused. It also
- * can be used to annotate a function, a variable, or a type that is
- * always unused.
- *
- * A callback interface may dictate that a function accepts a
- * parameter at that position, but the implementation of the function
- * may not need to use the parameter. In such a case, mark the parameter
- * with UNUSED.
- *
- * When a parameter may be used or unused, depending on conditional
- * compilation, consider using MAYBE_UNUSED instead.
- */
-#if GIT_GNUC_PREREQ(4, 5)
-#define UNUSED __attribute__((unused)) \
- __attribute__((deprecated ("parameter declared as UNUSED")))
-#elif defined(__GNUC__)
-#define UNUSED __attribute__((unused)) \
- __attribute__((deprecated))
-#else
-#define UNUSED
-#endif
-
-#if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
-# if !defined(_WIN32_WINNT)
-# define _WIN32_WINNT 0x0600
-# endif
-#define WIN32_LEAN_AND_MEAN /* stops windows.h including winsock.h */
-#include <winsock2.h>
-#ifndef NO_UNIX_SOCKETS
-#include <afunix.h>
-#endif
-#include <windows.h>
-#define GIT_WINDOWS_NATIVE
-#endif
-
#if defined(NO_UNIX_SOCKETS) || !defined(GIT_WINDOWS_NATIVE)
static inline int _have_unix_sockets(void)
{
@@ -253,45 +171,6 @@ static inline int _have_unix_sockets(void)
#define have_unix_sockets _have_unix_sockets
#endif
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <string.h>
-#ifdef HAVE_STRINGS_H
-#include <strings.h> /* for strcasecmp() */
-#endif
-#include <errno.h>
-#include <limits.h>
-#include <locale.h>
-#ifdef NEEDS_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/time.h>
-#include <time.h>
-#include <signal.h>
-#include <assert.h>
-#include <regex.h>
-#include <utime.h>
-#include <syslog.h>
-#if !defined(NO_POLL_H)
-#include <poll.h>
-#elif !defined(NO_SYS_POLL_H)
-#include <sys/poll.h>
-#else
-/* Pull the compat stuff */
-#include <poll.h>
-#endif
-#ifdef HAVE_BSD_SYSCTL
-#include <sys/sysctl.h>
-#endif
-
/* Used by compat/win32/path-utils.h, and more */
static inline int is_xplatform_dir_sep(int c)
{
@@ -308,48 +187,6 @@ static inline int is_xplatform_dir_sep(int c)
#elif defined(_MSC_VER)
#include "compat/win32/path-utils.h"
#include "compat/msvc.h"
-#else
-#include <sys/utsname.h>
-#include <sys/wait.h>
-#include <sys/resource.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/statvfs.h>
-#include <termios.h>
-#ifndef NO_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <sys/un.h>
-#ifndef NO_INTTYPES_H
-#include <inttypes.h>
-#else
-#include <stdint.h>
-#endif
-#ifdef HAVE_ARC4RANDOM_LIBBSD
-#include <bsd/stdlib.h>
-#endif
-#ifdef HAVE_GETRANDOM
-#include <sys/random.h>
-#endif
-#ifdef NO_INTPTR_T
-/*
- * On I16LP32, ILP32 and LP64 "long" is the safe bet, however
- * on LLP86, IL33LLP64 and P64 it needs to be "long long",
- * while on IP16 and IP16L32 it is "int" (resp. "short")
- * Size needs to match (or exceed) 'sizeof(void *)'.
- * We can't take "long long" here as not everybody has it.
- */
-typedef long intptr_t;
-typedef unsigned long uintptr_t;
-#endif
-#undef _ALL_SOURCE /* AIX 5.3L defines a struct list with _ALL_SOURCE. */
-#include <grp.h>
-#define _ALL_SOURCE 1
#endif
/* used on Mac OS X */
@@ -370,60 +207,6 @@ static inline const char *precompose_string_if_needed(const char *in)
#define probe_utf8_pathname_composition()
#endif
-#ifdef MKDIR_WO_TRAILING_SLASH
-#define mkdir(a,b) compat_mkdir_wo_trailing_slash((a),(b))
-int compat_mkdir_wo_trailing_slash(const char*, mode_t);
-#endif
-
-#ifdef time
-#undef time
-#endif
-static inline time_t git_time(time_t *tloc)
-{
- struct timeval tv;
-
- /*
- * Avoid time(NULL), which can disagree with gettimeofday(2)
- * and filesystem timestamps.
- */
- gettimeofday(&tv, NULL);
-
- if (tloc)
- *tloc = tv.tv_sec;
- return tv.tv_sec;
-}
-#define time git_time
-
-#ifdef NO_STRUCT_ITIMERVAL
-struct itimerval {
- struct timeval it_interval;
- struct timeval it_value;
-};
-#endif
-
-#ifdef NO_SETITIMER
-static inline int git_setitimer(int which UNUSED,
- const struct itimerval *value UNUSED,
- struct itimerval *newvalue UNUSED) {
- return 0; /* pretend success */
-}
-#undef setitimer
-#define setitimer(which,value,ovalue) git_setitimer(which,value,ovalue)
-#endif
-
-#ifndef NO_LIBGEN_H
-#include <libgen.h>
-#else
-#define basename gitbasename
-char *gitbasename(char *);
-#define dirname gitdirname
-char *gitdirname(char *);
-#endif
-
-#ifndef NO_ICONV
-#include <iconv.h>
-#endif
-
#ifndef NO_OPENSSL
#ifdef __APPLE__
#undef __AVAILABILITY_MACROS_USES_AVAILABILITY
@@ -441,34 +224,6 @@ char *gitdirname(char *);
# include <sys/sysinfo.h>
#endif
-/* On most systems <netdb.h> would have given us this, but
- * not on some systems (e.g. z/OS).
- */
-#ifndef NI_MAXHOST
-#define NI_MAXHOST 1025
-#endif
-
-#ifndef NI_MAXSERV
-#define NI_MAXSERV 32
-#endif
-
-/* On most systems <limits.h> would have given us this, but
- * not on some systems (e.g. GNU/Hurd).
- */
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-
-#ifndef NAME_MAX
-#define NAME_MAX 255
-#endif
-
-typedef uintmax_t timestamp_t;
-#define PRItime PRIuMAX
-#define parse_timestamp strtoumax
-#define TIME_MAX UINTMAX_MAX
-#define TIME_MIN 0
-
#ifndef PATH_SEP
#define PATH_SEP ':'
#endif
@@ -492,11 +247,6 @@ static inline int noop_core_config(const char *var UNUSED,
#define platform_core_config noop_core_config
#endif
-int lstat_cache_aware_rmdir(const char *path);
-#if !defined(__MINGW32__) && !defined(_MSC_VER)
-#define rmdir lstat_cache_aware_rmdir
-#endif
-
#ifndef has_dos_drive_prefix
static inline int git_has_dos_drive_prefix(const char *path UNUSED)
{
@@ -824,25 +574,6 @@ static inline bool strip_suffix(const char *str, const char *suffix,
memcpy(_swap_b_ptr, _swap_buffer, sizeof(a)); \
} while (0)
-#if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
-
-#ifndef PROT_READ
-#define PROT_READ 1
-#define PROT_WRITE 2
-#define MAP_PRIVATE 1
-#endif
-
-#define mmap git_mmap
-#define munmap git_munmap
-void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
-int git_munmap(void *start, size_t length);
-
-#else /* NO_MMAP || USE_WIN32_MMAP */
-
-#include <sys/mman.h>
-
-#endif /* NO_MMAP || USE_WIN32_MMAP */
-
#ifdef NO_MMAP
/* This value must be multiple of (pagesize * 2) */
@@ -858,177 +589,15 @@ int git_munmap(void *start, size_t length);
#endif /* NO_MMAP */
-#ifndef MAP_FAILED
-#define MAP_FAILED ((void *)-1)
-#endif
-
#ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
#define on_disk_bytes(st) ((st).st_size)
#else
#define on_disk_bytes(st) ((st).st_blocks * 512)
#endif
-#ifdef NEEDS_MODE_TRANSLATION
-#undef S_IFMT
-#undef S_IFREG
-#undef S_IFDIR
-#undef S_IFLNK
-#undef S_IFBLK
-#undef S_IFCHR
-#undef S_IFIFO
-#undef S_IFSOCK
-#define S_IFMT 0170000
-#define S_IFREG 0100000
-#define S_IFDIR 0040000
-#define S_IFLNK 0120000
-#define S_IFBLK 0060000
-#define S_IFCHR 0020000
-#define S_IFIFO 0010000
-#define S_IFSOCK 0140000
-#ifdef stat
-#undef stat
-#endif
-#define stat(path, buf) git_stat(path, buf)
-int git_stat(const char *, struct stat *);
-#ifdef fstat
-#undef fstat
-#endif
-#define fstat(fd, buf) git_fstat(fd, buf)
-int git_fstat(int, struct stat *);
-#ifdef lstat
-#undef lstat
-#endif
-#define lstat(path, buf) git_lstat(path, buf)
-int git_lstat(const char *, struct stat *);
-#endif
-
#define DEFAULT_PACKED_GIT_LIMIT \
((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? (32 * 1024L * 1024L) : 256))
-#ifdef NO_PREAD
-#define pread git_pread
-ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
-#endif
-
-#ifdef NO_SETENV
-#define setenv gitsetenv
-int gitsetenv(const char *, const char *, int);
-#endif
-
-#ifdef NO_MKDTEMP
-#define mkdtemp gitmkdtemp
-char *gitmkdtemp(char *);
-#endif
-
-#ifdef NO_UNSETENV
-#define unsetenv gitunsetenv
-int gitunsetenv(const char *);
-#endif
-
-#ifdef NO_STRCASESTR
-#define strcasestr gitstrcasestr
-char *gitstrcasestr(const char *haystack, const char *needle);
-#endif
-
-#ifdef NO_STRLCPY
-#define strlcpy gitstrlcpy
-size_t gitstrlcpy(char *, const char *, size_t);
-#endif
-
-#ifdef NO_STRTOUMAX
-#define strtoumax gitstrtoumax
-uintmax_t gitstrtoumax(const char *, char **, int);
-#define strtoimax gitstrtoimax
-intmax_t gitstrtoimax(const char *, char **, int);
-#endif
-
-#ifdef NO_HSTRERROR
-#define hstrerror githstrerror
-const char *githstrerror(int herror);
-#endif
-
-#ifdef NO_MEMMEM
-#define memmem gitmemmem
-void *gitmemmem(const void *haystack, size_t haystacklen,
- const void *needle, size_t needlelen);
-#endif
-
-#ifdef OVERRIDE_STRDUP
-#ifdef strdup
-#undef strdup
-#endif
-#define strdup gitstrdup
-char *gitstrdup(const char *s);
-#endif
-
-#ifdef NO_GETPAGESIZE
-#define getpagesize() sysconf(_SC_PAGESIZE)
-#endif
-
-#ifndef O_CLOEXEC
-#define O_CLOEXEC 0
-#endif
-
-#ifdef FREAD_READS_DIRECTORIES
-# if !defined(SUPPRESS_FOPEN_REDEFINITION)
-# ifdef fopen
-# undef fopen
-# endif
-# define fopen(a,b) git_fopen(a,b)
-# endif
-FILE *git_fopen(const char*, const char*);
-#endif
-
-#ifdef SNPRINTF_RETURNS_BOGUS
-#ifdef snprintf
-#undef snprintf
-#endif
-#define snprintf git_snprintf
-int git_snprintf(char *str, size_t maxsize,
- const char *format, ...);
-#ifdef vsnprintf
-#undef vsnprintf
-#endif
-#define vsnprintf git_vsnprintf
-int git_vsnprintf(char *str, size_t maxsize,
- const char *format, va_list ap);
-#endif
-
-#ifdef OPEN_RETURNS_EINTR
-#undef open
-#define open git_open_with_retry
-int git_open_with_retry(const char *path, int flag, ...);
-#endif
-
-#ifdef __GLIBC_PREREQ
-#if __GLIBC_PREREQ(2, 1)
-#define HAVE_STRCHRNUL
-#endif
-#endif
-
-#ifndef HAVE_STRCHRNUL
-#define strchrnul gitstrchrnul
-static inline char *gitstrchrnul(const char *s, int c)
-{
- while (*s && *s != c)
- s++;
- return (char *)s;
-}
-#endif
-
-#ifdef NO_INET_PTON
-int inet_pton(int af, const char *src, void *dst);
-#endif
-
-#ifdef NO_INET_NTOP
-const char *inet_ntop(int af, const void *src, char *dst, size_t size);
-#endif
-
-#ifdef NO_PTHREADS
-#define atexit git_atexit
-int git_atexit(void (*handler)(void));
-#endif
-
static inline size_t st_add(size_t a, size_t b)
{
if (unsigned_add_overflows(a, b))
@@ -1295,12 +864,6 @@ static inline size_t xsize_t(off_t len)
return (size_t) len;
}
-#ifndef HOST_NAME_MAX
-#define HOST_NAME_MAX 256
-#endif
-
-#include "sane-ctype.h"
-
/*
* Like skip_prefix, but compare case-insensitively. Note that the comparison
* is done via tolower(), so it is strictly ASCII (no multi-byte characters or
@@ -1366,34 +929,6 @@ static inline int strtol_i(char const *s, int base, int *result)
return 0;
}
-void git_stable_qsort(void *base, size_t nmemb, size_t size,
- int(*compar)(const void *, const void *));
-#ifdef INTERNAL_QSORT
-#define qsort git_stable_qsort
-#endif
-
-#define QSORT(base, n, compar) sane_qsort((base), (n), sizeof(*(base)), compar)
-static inline void sane_qsort(void *base, size_t nmemb, size_t size,
- int(*compar)(const void *, const void *))
-{
- if (nmemb > 1)
- qsort(base, nmemb, size, compar);
-}
-
-#define STABLE_QSORT(base, n, compar) \
- git_stable_qsort((base), (n), sizeof(*(base)), compar)
-
-#ifndef HAVE_ISO_QSORT_S
-int git_qsort_s(void *base, size_t nmemb, size_t size,
- int (*compar)(const void *, const void *, void *), void *ctx);
-#define qsort_s git_qsort_s
-#endif
-
-#define QSORT_S(base, n, compar, ctx) do { \
- if (qsort_s((base), (n), sizeof(*(base)), compar, ctx)) \
- BUG("qsort_s() failed"); \
-} while (0)
-
#ifndef REG_STARTEND
#error "Git requires REG_STARTEND support. Compile with NO_REGEX=NeedsStartEnd"
#endif
@@ -1418,39 +953,12 @@ int git_regcomp(regex_t *preg, const char *pattern, int cflags);
# define FORCE_DIR_SET_GID 0
#endif
-#ifdef NO_NSEC
-#undef USE_NSEC
-#define ST_CTIME_NSEC(st) 0
-#define ST_MTIME_NSEC(st) 0
-#else
-#ifdef USE_ST_TIMESPEC
-#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec))
-#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec))
-#else
-#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec))
-#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec))
-#endif
-#endif
-
#ifdef UNRELIABLE_FSTAT
#define fstat_is_reliable() 0
#else
#define fstat_is_reliable() 1
#endif
-#ifndef va_copy
-/*
- * Since an obvious implementation of va_list would be to make it a
- * pointer into the stack frame, a simple assignment will work on
- * many systems. But let's try to be more portable.
- */
-#ifdef __va_copy
-#define va_copy(dst, src) __va_copy(dst, src)
-#else
-#define va_copy(dst, src) ((dst) = (src))
-#endif
-#endif
-
/* usage.c: only to be used for testing BUG() implementation (see test-tool) */
extern int BUG_exit_code;
@@ -1460,6 +968,8 @@ extern int bug_called_must_BUG;
__attribute__((format (printf, 3, 4))) NORETURN
void BUG_fl(const char *file, int line, const char *fmt, ...);
#define BUG(...) BUG_fl(__FILE__, __LINE__, __VA_ARGS__)
+/* ASSERT: like assert(), but won't be compiled out with NDEBUG */
+#define ASSERT(a) if (!(a)) BUG("Assertion `" #a "' failed.")
__attribute__((format (printf, 3, 4)))
void bug_fl(const char *file, int line, const char *fmt, ...);
#define bug(...) bug_fl(__FILE__, __LINE__, __VA_ARGS__)
@@ -1480,41 +990,6 @@ void bug_fl(const char *file, int line, const char *fmt, ...);
# define SHELL_PATH "/bin/sh"
#endif
-#ifndef _POSIX_THREAD_SAFE_FUNCTIONS
-static inline void git_flockfile(FILE *fh UNUSED)
-{
- ; /* nothing */
-}
-static inline void git_funlockfile(FILE *fh UNUSED)
-{
- ; /* nothing */
-}
-#undef flockfile
-#undef funlockfile
-#undef getc_unlocked
-#define flockfile(fh) git_flockfile(fh)
-#define funlockfile(fh) git_funlockfile(fh)
-#define getc_unlocked(fh) getc(fh)
-#endif
-
-#ifdef FILENO_IS_A_MACRO
-int git_fileno(FILE *stream);
-# ifndef COMPAT_CODE_FILENO
-# undef fileno
-# define fileno(p) git_fileno(p)
-# endif
-#endif
-
-#ifdef NEED_ACCESS_ROOT_HANDLER
-int git_access(const char *path, int mode);
-# ifndef COMPAT_CODE_ACCESS
-# ifdef access
-# undef access
-# endif
-# define access(path, mode) git_access(path, mode)
-# endif
-#endif
-
/*
* Our code often opens a path to an optional file, to work on its
* contents when we can successfully open it. We can ignore a failure
@@ -1583,4 +1058,20 @@ static inline void *container_of_or_null_offset(void *ptr, size_t offset)
((uintptr_t)&(ptr)->member - (uintptr_t)(ptr))
#endif /* !__GNUC__ */
+/*
+ * Prevent an overly clever compiler from optimizing an expression
+ * out, triggering a false positive when building with the
+ * -Wunreachable-code option. false_but_the_compiler_does_not_know_it_
+ * is defined in a compilation unit separate from where the macro is
+ * used, initialized to 0, and never modified.
+ */
+#define NOT_CONSTANT(expr) ((expr) || false_but_the_compiler_does_not_know_it_)
+extern int false_but_the_compiler_does_not_know_it_;
+
+#ifdef CHECK_ASSERTION_SIDE_EFFECTS
+#undef assert
+extern int not_supposed_to_survive;
+#define assert(expr) ((void)(not_supposed_to_survive || (expr)))
+#endif /* CHECK_ASSERTION_SIDE_EFFECTS */
+
#endif
diff --git a/git-curl-compat.h b/git-curl-compat.h
index 703756ba85..aa8eed7ed2 100644
--- a/git-curl-compat.h
+++ b/git-curl-compat.h
@@ -45,4 +45,11 @@
#define GIT_CURL_HAVE_CURLOPT_PROTOCOLS_STR 1
#endif
+/**
+ * CURLOPT_TCP_KEEPCNT was added in 8.9.0, released in July, 2024.
+ */
+#if LIBCURL_VERSION_NUM >= 0x080900
+#define GIT_CURL_HAVE_CURLOPT_TCP_KEEPCNT
+#endif
+
#endif
diff --git a/git-zlib.c b/git-zlib.c
index 651dd9e07c..df9604910e 100644
--- a/git-zlib.c
+++ b/git-zlib.c
@@ -45,7 +45,7 @@ static void zlib_pre_call(git_zstream *s)
s->z.avail_out = zlib_buf_cap(s->avail_out);
}
-static void zlib_post_call(git_zstream *s)
+static void zlib_post_call(git_zstream *s, int status)
{
unsigned long bytes_consumed;
unsigned long bytes_produced;
@@ -54,7 +54,12 @@ static void zlib_post_call(git_zstream *s)
bytes_produced = s->z.next_out - s->next_out;
if (s->z.total_out != s->total_out + bytes_produced)
BUG("total_out mismatch");
- if (s->z.total_in != s->total_in + bytes_consumed)
+ /*
+ * zlib does not update total_in when it returns Z_NEED_DICT,
+ * causing a mismatch here. Skip the sanity check in that case.
+ */
+ if (status != Z_NEED_DICT &&
+ s->z.total_in != s->total_in + bytes_consumed)
BUG("total_in mismatch");
s->total_out = s->z.total_out;
@@ -72,7 +77,7 @@ void git_inflate_init(git_zstream *strm)
zlib_pre_call(strm);
status = inflateInit(&strm->z);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
if (status == Z_OK)
return;
die("inflateInit: %s (%s)", zerr_to_string(status),
@@ -90,7 +95,7 @@ void git_inflate_init_gzip_only(git_zstream *strm)
zlib_pre_call(strm);
status = inflateInit2(&strm->z, windowBits);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
if (status == Z_OK)
return;
die("inflateInit2: %s (%s)", zerr_to_string(status),
@@ -103,7 +108,7 @@ void git_inflate_end(git_zstream *strm)
zlib_pre_call(strm);
status = inflateEnd(&strm->z);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
if (status == Z_OK)
return;
error("inflateEnd: %s (%s)", zerr_to_string(status),
@@ -122,7 +127,7 @@ int git_inflate(git_zstream *strm, int flush)
? 0 : flush);
if (status == Z_MEM_ERROR)
die("inflate: out of memory");
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
/*
* Let zlib work another round, while we can still
@@ -160,7 +165,7 @@ void git_deflate_init(git_zstream *strm, int level)
memset(strm, 0, sizeof(*strm));
zlib_pre_call(strm);
status = deflateInit(&strm->z, level);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
if (status == Z_OK)
return;
die("deflateInit: %s (%s)", zerr_to_string(status),
@@ -176,7 +181,7 @@ static void do_git_deflate_init(git_zstream *strm, int level, int windowBits)
status = deflateInit2(&strm->z, level,
Z_DEFLATED, windowBits,
8, Z_DEFAULT_STRATEGY);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
if (status == Z_OK)
return;
die("deflateInit2: %s (%s)", zerr_to_string(status),
@@ -207,7 +212,7 @@ int git_deflate_abort(git_zstream *strm)
zlib_pre_call(strm);
status = deflateEnd(&strm->z);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
return status;
}
@@ -227,7 +232,7 @@ int git_deflate_end_gently(git_zstream *strm)
zlib_pre_call(strm);
status = deflateEnd(&strm->z);
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
return status;
}
@@ -244,7 +249,7 @@ int git_deflate(git_zstream *strm, int flush)
? 0 : flush);
if (status == Z_MEM_ERROR)
die("deflate: out of memory");
- zlib_post_call(strm);
+ zlib_post_call(strm, status);
/*
* Let zlib work another round, while we can still
diff --git a/git.c b/git.c
index 450d6aaa86..77c4359522 100644
--- a/git.c
+++ b/git.c
@@ -541,6 +541,7 @@ static struct cmd_struct commands[] = {
{ "diff", cmd_diff, NO_PARSEOPT },
{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
{ "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
+ { "diff-pairs", cmd_diff_pairs, RUN_SETUP | NO_PARSEOPT },
{ "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT },
{ "difftool", cmd_difftool, RUN_SETUP_GENTLY },
{ "fast-export", cmd_fast_export, RUN_SETUP },
diff --git a/hash.h b/hash.h
index 4367acfec5..5e3c462dc5 100644
--- a/hash.h
+++ b/hash.h
@@ -193,17 +193,18 @@ struct object_id {
int algo; /* XXX requires 4-byte alignment */
};
-#define GET_OID_QUIETLY 01
-#define GET_OID_COMMIT 02
-#define GET_OID_COMMITTISH 04
-#define GET_OID_TREE 010
-#define GET_OID_TREEISH 020
-#define GET_OID_BLOB 040
-#define GET_OID_FOLLOW_SYMLINKS 0100
-#define GET_OID_RECORD_PATH 0200
-#define GET_OID_ONLY_TO_DIE 04000
-#define GET_OID_REQUIRE_PATH 010000
-#define GET_OID_HASH_ANY 020000
+#define GET_OID_QUIETLY 01
+#define GET_OID_COMMIT 02
+#define GET_OID_COMMITTISH 04
+#define GET_OID_TREE 010
+#define GET_OID_TREEISH 020
+#define GET_OID_BLOB 040
+#define GET_OID_FOLLOW_SYMLINKS 0100
+#define GET_OID_RECORD_PATH 0200
+#define GET_OID_ONLY_TO_DIE 04000
+#define GET_OID_REQUIRE_PATH 010000
+#define GET_OID_HASH_ANY 020000
+#define GET_OID_SKIP_AMBIGUITY_CHECK 040000
#define GET_OID_DISAMBIGUATORS \
(GET_OID_COMMIT | GET_OID_COMMITTISH | \
diff --git a/help.c b/help.c
index 8d91afe851..c54bd9918a 100644
--- a/help.c
+++ b/help.c
@@ -2,6 +2,7 @@
#define DISABLE_SIGN_COMPARE_WARNINGS
#include "git-compat-util.h"
+#include "git-zlib.h"
#include "config.h"
#include "builtin.h"
#include "exec-cmd.h"
@@ -797,7 +798,9 @@ void get_version_info(struct strbuf *buf, int show_build_options)
#if defined OPENSSL_VERSION_TEXT
strbuf_addf(buf, "OpenSSL: %s\n", OPENSSL_VERSION_TEXT);
#endif
-#if defined ZLIB_VERSION
+#if defined ZLIBNG_VERSION
+ strbuf_addf(buf, "zlib-ng: %s\n", ZLIBNG_VERSION);
+#elif defined ZLIB_VERSION
strbuf_addf(buf, "zlib: %s\n", ZLIB_VERSION);
#endif
}
diff --git a/hook.c b/hook.c
index 9ddbdee06d..b3de1048bf 100644
--- a/hook.c
+++ b/hook.c
@@ -16,8 +16,7 @@ const char *find_hook(struct repository *r, const char *name)
int found_hook;
- strbuf_reset(&path);
- strbuf_repo_git_path(&path, r, "hooks/%s", name);
+ repo_git_path_replace(r, &path, "hooks/%s", name);
found_hook = access(path.buf, X_OK) >= 0;
#ifdef STRIP_EXTENSION
if (!found_hook) {
diff --git a/http-backend.c b/http-backend.c
index 33cf378282..50b2858fad 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -183,7 +183,7 @@ static void send_strbuf(struct strbuf *hdr,
static void send_local_file(struct strbuf *hdr, const char *the_type,
const char *name)
{
- char *p = git_pathdup("%s", name);
+ char *p = repo_git_path(the_repository, "%s", name);
size_t buf_alloc = 8192;
char *buf = xmalloc(buf_alloc);
int fd;
diff --git a/http.c b/http.c
index 0c9a872809..d21e3a3bad 100644
--- a/http.c
+++ b/http.c
@@ -104,6 +104,10 @@ static struct {
};
#endif
+static long curl_tcp_keepidle = -1;
+static long curl_tcp_keepintvl = -1;
+static long curl_tcp_keepcnt = -1;
+
enum proactive_auth {
PROACTIVE_AUTH_NONE = 0,
PROACTIVE_AUTH_IF_CREDENTIALS,
@@ -438,11 +442,11 @@ static int http_options(const char *var, const char *value,
return 0;
}
if (!strcmp("http.lowspeedlimit", var)) {
- curl_low_speed_limit = (long)git_config_int(var, value, ctx->kvi);
+ curl_low_speed_limit = git_config_int(var, value, ctx->kvi);
return 0;
}
if (!strcmp("http.lowspeedtime", var)) {
- curl_low_speed_time = (long)git_config_int(var, value, ctx->kvi);
+ curl_low_speed_time = git_config_int(var, value, ctx->kvi);
return 0;
}
@@ -557,6 +561,19 @@ static int http_options(const char *var, const char *value,
return 0;
}
+ if (!strcmp("http.keepaliveidle", var)) {
+ curl_tcp_keepidle = git_config_int(var, value, ctx->kvi);
+ return 0;
+ }
+ if (!strcmp("http.keepaliveinterval", var)) {
+ curl_tcp_keepintvl = git_config_int(var, value, ctx->kvi);
+ return 0;
+ }
+ if (!strcmp("http.keepalivecount", var)) {
+ curl_tcp_keepcnt = git_config_int(var, value, ctx->kvi);
+ return 0;
+ }
+
/* Fall back on the default ones */
return git_default_config(var, value, ctx, data);
}
@@ -704,11 +721,6 @@ static int has_proxy_cert_password(void)
return 1;
}
-static void set_curl_keepalive(CURL *c)
-{
- curl_easy_setopt(c, CURLOPT_TCP_KEEPALIVE, 1);
-}
-
/* Return 1 if redactions have been made, 0 otherwise. */
static int redact_sensitive_header(struct strbuf *header, size_t offset)
{
@@ -1242,7 +1254,18 @@ static CURL *get_curl_handle(void)
}
init_curl_proxy_auth(result);
- set_curl_keepalive(result);
+ curl_easy_setopt(result, CURLOPT_TCP_KEEPALIVE, 1);
+
+ if (curl_tcp_keepidle > -1)
+ curl_easy_setopt(result, CURLOPT_TCP_KEEPIDLE,
+ curl_tcp_keepidle);
+ if (curl_tcp_keepintvl > -1)
+ curl_easy_setopt(result, CURLOPT_TCP_KEEPINTVL,
+ curl_tcp_keepintvl);
+#ifdef GIT_CURL_HAVE_CURLOPT_TCP_KEEPCNT
+ if (curl_tcp_keepcnt > -1)
+ curl_easy_setopt(result, CURLOPT_TCP_KEEPCNT, curl_tcp_keepcnt);
+#endif
return result;
}
@@ -1256,10 +1279,30 @@ static void set_from_env(char **var, const char *envname)
}
}
+static void set_long_from_env(long *var, const char *envname)
+{
+ const char *val = getenv(envname);
+ if (val) {
+ long tmp;
+ char *endp;
+ int saved_errno = errno;
+
+ errno = 0;
+ tmp = strtol(val, &endp, 10);
+
+ if (errno)
+ warning_errno(_("failed to parse %s"), envname);
+ else if (*endp || endp == val)
+ warning(_("failed to parse %s"), envname);
+ else
+ *var = tmp;
+
+ errno = saved_errno;
+ }
+}
+
void http_init(struct remote *remote, const char *url, int proactive_auth)
{
- char *low_speed_limit;
- char *low_speed_time;
char *normalized_url;
struct urlmatch_config config = URLMATCH_CONFIG_INIT;
@@ -1338,12 +1381,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
set_from_env(&user_agent, "GIT_HTTP_USER_AGENT");
- low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
- if (low_speed_limit)
- curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
- low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME");
- if (low_speed_time)
- curl_low_speed_time = strtol(low_speed_time, NULL, 10);
+ set_long_from_env(&curl_low_speed_limit, "GIT_HTTP_LOW_SPEED_LIMIT");
+ set_long_from_env(&curl_low_speed_time, "GIT_HTTP_LOW_SPEED_TIME");
if (curl_ssl_verify == -1)
curl_ssl_verify = 1;
@@ -1370,6 +1409,10 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
ssl_cert_password_required = 1;
}
+ set_long_from_env(&curl_tcp_keepidle, "GIT_TCP_KEEPIDLE");
+ set_long_from_env(&curl_tcp_keepintvl, "GIT_TCP_KEEPINTVL");
+ set_long_from_env(&curl_tcp_keepcnt, "GIT_TCP_KEEPCNT");
+
curl_default = get_curl_handle();
}
diff --git a/ident.c b/ident.c
index caf41fb2a9..967895d885 100644
--- a/ident.c
+++ b/ident.c
@@ -59,7 +59,7 @@ static struct passwd *xgetpwuid_self(int *is_bogus)
static void copy_gecos(const struct passwd *w, struct strbuf *name)
{
- char *src;
+ const char *src;
/* Traditionally GECOS field had office phone numbers etc, separated
* with commas. Also & stands for capitalized form of the login name.
diff --git a/imap-send.c b/imap-send.c
index 6c8f84e836..27dc033c7f 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -324,6 +324,8 @@ static int ssl_socket_connect(struct imap_socket *sock,
cert = SSL_get_peer_certificate(sock->ssl);
if (!cert)
return error("unable to get peer certificate.");
+ if (SSL_get_verify_result(sock->ssl) != X509_V_OK)
+ return error("unable to verify peer certificate");
if (verify_hostname(cert, cfg->host) < 0)
return -1;
}
diff --git a/iterator.h b/iterator.h
index 0f6900e43a..6b77dcc262 100644
--- a/iterator.h
+++ b/iterator.h
@@ -12,7 +12,7 @@
#define ITER_OK 0
/*
- * The iterator is exhausted and has been freed.
+ * The iterator is exhausted.
*/
#define ITER_DONE -1
diff --git a/list-objects-filter-options.h b/list-objects-filter-options.h
index 55fab8563d..7b2108b986 100644
--- a/list-objects-filter-options.h
+++ b/list-objects-filter-options.h
@@ -82,7 +82,7 @@ void list_objects_filter_init(struct list_objects_filter_options *filter_options
* "filter" SP <arg>
*
* The filter keyword will be used by many commands.
- * See Documentation/rev-list-options.txt for allowed values for <arg>.
+ * See Documentation/rev-list-options.adoc for allowed values for <arg>.
*
* Capture the given arg as the "filter_spec". This can be forwarded to
* subordinate commands when necessary (although it's better to pass it through
diff --git a/loose.c b/loose.c
index 897ba389da..bb602aaa36 100644
--- a/loose.c
+++ b/loose.c
@@ -75,7 +75,7 @@ static int load_one_loose_object_map(struct repository *repo, struct object_dire
insert_loose_map(dir, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
insert_loose_map(dir, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
- strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+ repo_common_path_replace(repo, &path, "objects/loose-object-idx");
fp = fopen(path.buf, "rb");
if (!fp) {
strbuf_release(&path);
@@ -133,7 +133,7 @@ int repo_write_loose_object_map(struct repository *repo)
if (!should_use_loose_object_map(repo))
return 0;
- strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+ repo_common_path_replace(repo, &path, "objects/loose-object-idx");
fd = hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
iter = kh_begin(map);
if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
@@ -174,7 +174,7 @@ static int write_one_object(struct repository *repo, const struct object_id *oid
struct stat st;
struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
- strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
+ repo_common_path_replace(repo, &path, "objects/loose-object-idx");
hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
@@ -190,7 +190,7 @@ static int write_one_object(struct repository *repo, const struct object_id *oid
goto errout;
if (close(fd))
goto errout;
- adjust_shared_perm(path.buf);
+ adjust_shared_perm(repo, path.buf);
rollback_lock_file(&lock);
strbuf_release(&buf);
strbuf_release(&path);
diff --git a/merge-ort-wrappers.c b/merge-ort-wrappers.c
index d6f6135996..c54d56b344 100644
--- a/merge-ort-wrappers.c
+++ b/merge-ort-wrappers.c
@@ -1,9 +1,13 @@
#include "git-compat-util.h"
#include "gettext.h"
#include "hash.h"
+#include "hex.h"
+#include "lockfile.h"
#include "merge-ort.h"
#include "merge-ort-wrappers.h"
#include "read-cache-ll.h"
+#include "repository.h"
+#include "tag.h"
#include "tree.h"
#include "commit.h"
@@ -29,6 +33,7 @@ int merge_ort_nonrecursive(struct merge_options *opt,
struct tree *merge_base)
{
struct merge_result result;
+ int show_msgs;
if (unclean(opt, head))
return -1;
@@ -38,9 +43,10 @@ int merge_ort_nonrecursive(struct merge_options *opt,
return 1;
}
+ show_msgs = !!opt->verbosity;
memset(&result, 0, sizeof(result));
merge_incore_nonrecursive(opt, merge_base, head, merge, &result);
- merge_switch_to_result(opt, head, &result, 1, 1);
+ merge_switch_to_result(opt, head, &result, 1, show_msgs);
return result.clean;
}
@@ -53,14 +59,76 @@ int merge_ort_recursive(struct merge_options *opt,
{
struct tree *head = repo_get_commit_tree(opt->repo, side1);
struct merge_result tmp;
+ int show_msgs;
if (unclean(opt, head))
return -1;
+ show_msgs = !!opt->verbosity;
memset(&tmp, 0, sizeof(tmp));
merge_incore_recursive(opt, merge_bases, side1, side2, &tmp);
- merge_switch_to_result(opt, head, &tmp, 1, 1);
+ merge_switch_to_result(opt, head, &tmp, 1, show_msgs);
*result = NULL;
return tmp.clean;
}
+
+static struct commit *get_ref(struct repository *repo,
+ const struct object_id *oid,
+ const char *name)
+{
+ struct object *object;
+
+ object = deref_tag(repo, parse_object(repo, oid),
+ name, strlen(name));
+ if (!object)
+ return NULL;
+ if (object->type == OBJ_TREE)
+ return make_virtual_commit(repo, (struct tree*)object, name);
+ if (object->type != OBJ_COMMIT)
+ return NULL;
+ if (repo_parse_commit(repo, (struct commit *)object))
+ return NULL;
+ return (struct commit *)object;
+}
+
+int merge_ort_generic(struct merge_options *opt,
+ const struct object_id *head,
+ const struct object_id *merge,
+ int num_merge_bases,
+ const struct object_id *merge_bases,
+ struct commit **result)
+{
+ int clean;
+ struct lock_file lock = LOCK_INIT;
+ struct commit *head_commit = get_ref(opt->repo, head, opt->branch1);
+ struct commit *next_commit = get_ref(opt->repo, merge, opt->branch2);
+ struct commit_list *ca = NULL;
+
+ if (merge_bases) {
+ int i;
+ for (i = 0; i < num_merge_bases; ++i) {
+ struct commit *base;
+ if (!(base = get_ref(opt->repo, &merge_bases[i],
+ oid_to_hex(&merge_bases[i]))))
+ return error(_("Could not parse object '%s'"),
+ oid_to_hex(&merge_bases[i]));
+ commit_list_insert(base, &ca);
+ }
+ }
+
+ repo_hold_locked_index(opt->repo, &lock, LOCK_DIE_ON_ERROR);
+ clean = merge_ort_recursive(opt, head_commit, next_commit, ca,
+ result);
+ free_commit_list(ca);
+ if (clean < 0) {
+ rollback_lock_file(&lock);
+ return clean;
+ }
+
+ if (write_locked_index(opt->repo->index, &lock,
+ COMMIT_LOCK | SKIP_IF_UNCHANGED))
+ return error(_("Unable to write index."));
+
+ return clean ? 0 : 1;
+}
diff --git a/merge-ort-wrappers.h b/merge-ort-wrappers.h
index 90af1f69c5..aeffa1c87b 100644
--- a/merge-ort-wrappers.h
+++ b/merge-ort-wrappers.h
@@ -22,4 +22,16 @@ int merge_ort_recursive(struct merge_options *opt,
const struct commit_list *ancestors,
struct commit **result);
+/*
+ * rename-detecting three-way merge. num_merge_bases must be at least 1.
+ * Recursive ancestor consolidation will be performed if num_merge_bases > 1.
+ * Wrapper mimicking the old merge_recursive_generic() function.
+ */
+int merge_ort_generic(struct merge_options *opt,
+ const struct object_id *head,
+ const struct object_id *merge,
+ int num_merge_bases,
+ const struct object_id *merge_bases,
+ struct commit **result);
+
#endif
diff --git a/merge-ort.c b/merge-ort.c
index 46e78c3ffa..c41f466577 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -791,7 +791,7 @@ static void path_msg(struct merge_options *opt,
struct strbuf tmp = STRBUF_INIT;
/* Sanity checks */
- assert(omittable_hint ==
+ ASSERT(omittable_hint ==
(!starts_with(type_short_descriptions[type], "CONFLICT") &&
!starts_with(type_short_descriptions[type], "ERROR")) ||
type == CONFLICT_DIR_RENAME_SUGGESTED);
@@ -1517,8 +1517,8 @@ static int handle_deferred_entries(struct merge_options *opt,
struct strintmap copy;
/* Loop over the set of paths we need to know rename info for */
- strset_for_each_entry(&renames->relevant_sources[side],
- &iter, entry) {
+ strintmap_for_each_entry(&renames->relevant_sources[side],
+ &iter, entry) {
char *rename_target, *dir, *dir_marker;
struct strmap_entry *e;
@@ -1642,7 +1642,7 @@ static int handle_deferred_entries(struct merge_options *opt,
ci = strmap_get(&opt->priv->paths, path);
VERIFY_CI(ci);
- assert(renames->deferred[side].trivial_merges_okay &&
+ ASSERT(renames->deferred[side].trivial_merges_okay &&
!strset_contains(&renames->deferred[side].target_dirs,
path));
resolve_trivial_directory_merge(ci, side);
@@ -3048,7 +3048,8 @@ static int process_renames(struct merge_options *opt,
}
}
- assert(source_deleted || oldinfo->filemask & old_sidemask);
+ assert(source_deleted || oldinfo->filemask & old_sidemask ||
+ !strcmp(pair->one->path, pair->two->path));
/* Need to check for special types of rename conflicts... */
if (collision && !source_deleted) {
@@ -3404,6 +3405,11 @@ static int collect_renames(struct merge_options *opt,
pool_diff_free_filepair(&opt->priv->pool, p);
continue;
}
+ if (opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_NONE &&
+ p->status == 'R' && 1) {
+ possibly_cache_new_pair(renames, p, side_index, NULL);
+ goto skip_directory_renames;
+ }
new_path = check_for_directory_rename(opt, p->two->path,
side_index,
@@ -3421,11 +3427,12 @@ static int collect_renames(struct merge_options *opt,
if (new_path)
apply_directory_rename_modifications(opt, p, new_path);
+skip_directory_renames:
/*
* p->score comes back from diffcore_rename_extended() with
- * the similarity of the renamed file. The similarity is
- * was used to determine that the two files were related
- * and are a rename, which we have already used, but beyond
+ * the similarity of the renamed file. The similarity was
+ * used to determine that the two files were related and
+ * are a rename, which we have already used, but beyond
* that we have no use for the similarity. So p->score is
* now irrelevant. However, process_renames() will need to
* know which side of the merge this rename was associated
@@ -3448,6 +3455,11 @@ static int detect_and_process_renames(struct merge_options *opt)
if (!possible_renames(renames))
goto cleanup;
+ if (!opt->detect_renames) {
+ renames->redo_after_renames = 0;
+ renames->cached_pairs_valid_side = 0;
+ goto cleanup;
+ }
trace2_region_enter("merge", "regular renames", opt->repo);
detection_run |= detect_regular_renames(opt, MERGE_SIDE1);
@@ -4878,9 +4890,9 @@ static inline void set_commit_tree(struct commit *c, struct tree *t)
c->maybe_tree = t;
}
-static struct commit *make_virtual_commit(struct repository *repo,
- struct tree *tree,
- const char *comment)
+struct commit *make_virtual_commit(struct repository *repo,
+ struct tree *tree,
+ const char *comment)
{
struct commit *commit = alloc_commit_node(repo);
@@ -5020,7 +5032,8 @@ static void merge_start(struct merge_options *opt, struct merge_result *result)
trace2_region_leave("merge", "allocate/init", opt->repo);
}
-static void merge_check_renames_reusable(struct merge_result *result,
+static void merge_check_renames_reusable(struct merge_options *opt,
+ struct merge_result *result,
struct tree *merge_base,
struct tree *side1,
struct tree *side2)
@@ -5046,6 +5059,26 @@ static void merge_check_renames_reusable(struct merge_result *result,
}
/*
+ * Avoid using cached renames when directory rename detection is
+ * turned off. Cached renames are far less important in that case,
+ * and they lead to testcases with an interesting intersection of
+ * effects from relevant renames optimization, trivial directory
+ * resolution optimization, and cached renames all converging when
+ * the target of a cached rename is in a directory that
+ * collect_merge_info() does not recurse into. To avoid such
+ * problems, simply disable cached renames for this case (similar
+ * to the rename/rename(1to1) case; see the "disabling the
+ * optimization" comment near that case).
+ *
+ * This could be revisited in the future; see the commit message
+ * where this comment was added for some possible pointers.
+ */
+ if (opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_NONE) {
+ renames->cached_pairs_valid_side = 0; /* neither side valid */
+ return;
+ }
+
+ /*
* Handle other cases; note that merge_trees[0..2] will only
* be NULL if opti is, or if all three were manually set to
* NULL by e.g. rename/rename(1to1) handling.
@@ -5186,6 +5219,8 @@ static void merge_ort_internal(struct merge_options *opt,
ancestor_name = "empty tree";
} else if (merge_bases) {
ancestor_name = "merged common ancestors";
+ } else if (opt->ancestor) {
+ ancestor_name = opt->ancestor;
} else {
strbuf_add_unique_abbrev(&merge_base_abbrev,
&merged_merge_bases->object.oid,
@@ -5251,7 +5286,7 @@ void merge_incore_nonrecursive(struct merge_options *opt,
trace2_region_enter("merge", "merge_start", opt->repo);
assert(opt->ancestor != NULL);
- merge_check_renames_reusable(result, merge_base, side1, side2);
+ merge_check_renames_reusable(opt, result, merge_base, side1, side2);
merge_start(opt, result);
/*
* Record the trees used in this merge, so if there's a next merge in
@@ -5275,8 +5310,13 @@ void merge_incore_recursive(struct merge_options *opt,
{
trace2_region_enter("merge", "incore_recursive", opt->repo);
- /* We set the ancestor label based on the merge_bases */
- assert(opt->ancestor == NULL);
+ /*
+ * We set the ancestor label based on the merge_bases...but we
+ * allow one exception through so that builtin/am can override
+ * with its constructed fake ancestor.
+ */
+ assert(opt->ancestor == NULL ||
+ (merge_bases && !merge_bases->next));
trace2_region_enter("merge", "merge_start", opt->repo);
merge_start(opt, result);
diff --git a/merge-ort.h b/merge-ort.h
index 82f2b3222d..b63bc5424e 100644
--- a/merge-ort.h
+++ b/merge-ort.h
@@ -44,6 +44,11 @@ struct merge_result {
unsigned _properly_initialized;
};
+/* Mostly internal function also used by merge-ort-wrappers.c */
+struct commit *make_virtual_commit(struct repository *repo,
+ struct tree *tree,
+ const char *comment);
+
/*
* rename-detecting three-way merge with recursive ancestor consolidation.
* working tree and index are untouched.
diff --git a/merge-recursive.c b/merge-recursive.c
index 884ccf99a5..4fbbece922 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1197,7 +1197,7 @@ static void print_commit(struct repository *repo, struct commit *commit)
struct pretty_print_context ctx = {0};
ctx.date_mode.type = DATE_NORMAL;
/* FIXME: Merge this with output_commit_title() */
- assert(!merge_remote_util(commit));
+ ASSERT(!merge_remote_util(commit));
repo_format_commit_message(repo, commit, " %h: %m %s", &sb, &ctx);
fprintf(stderr, "%s\n", sb.buf);
strbuf_release(&sb);
diff --git a/mergetools/vimdiff b/mergetools/vimdiff
index ffc9be86c8..78710858e8 100644
--- a/mergetools/vimdiff
+++ b/mergetools/vimdiff
@@ -305,6 +305,9 @@ gen_cmd () {
elif echo "$LAYOUT" | grep @BASE >/dev/null
then
FINAL_TARGET="BASE"
+ elif echo "$LAYOUT" | grep @REMOTE >/dev/null
+ then
+ FINAL_TARGET="REMOTE"
else
FINAL_TARGET="MERGED"
fi
@@ -529,7 +532,7 @@ run_unit_tests () {
# Function to make sure that we don't break anything when modifying this
# script.
- NUMBER_OF_TEST_CASES=16
+ NUMBER_OF_TEST_CASES=19
TEST_CASE_01="(LOCAL,BASE,REMOTE)/MERGED" # default behaviour
TEST_CASE_02="@LOCAL,REMOTE" # when using vimdiff1
@@ -547,6 +550,9 @@ run_unit_tests () {
TEST_CASE_14="BASE,REMOTE+BASE,LOCAL"
TEST_CASE_15=" (( (LOCAL , BASE , REMOTE) / MERGED)) +(BASE) , LOCAL+ BASE , REMOTE+ (((LOCAL / BASE / REMOTE)) , MERGED ) "
TEST_CASE_16="LOCAL,BASE,REMOTE / MERGED + BASE,LOCAL + BASE,REMOTE + (LOCAL / BASE / REMOTE),MERGED"
+ TEST_CASE_17="(LOCAL,@BASE,REMOTE)/MERGED"
+ TEST_CASE_18="LOCAL,@REMOTE"
+ TEST_CASE_19="@REMOTE"
EXPECTED_CMD_01="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | execute 'tabdo windo diffthis' | tabfirst\""
EXPECTED_CMD_02="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 1b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\""
@@ -564,6 +570,9 @@ run_unit_tests () {
EXPECTED_CMD_14="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | execute 'tabdo windo diffthis' | tabfirst\""
EXPECTED_CMD_15="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\""
EXPECTED_CMD_16="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\""
+ EXPECTED_CMD_17="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | execute 'tabdo windo diffthis' | tabfirst\""
+ EXPECTED_CMD_18="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 1b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\""
+ EXPECTED_CMD_19="-c \"set hidden diffopt-=hiddenoff | echo | silent execute 'bufdo diffthis' | 3b | execute 'tabdo windo diffthis' | tabfirst\""
EXPECTED_TARGET_01="MERGED"
EXPECTED_TARGET_02="LOCAL"
@@ -581,6 +590,9 @@ run_unit_tests () {
EXPECTED_TARGET_14="MERGED"
EXPECTED_TARGET_15="MERGED"
EXPECTED_TARGET_16="MERGED"
+ EXPECTED_TARGET_17="BASE"
+ EXPECTED_TARGET_18="REMOTE"
+ EXPECTED_TARGET_19="REMOTE"
at_least_one_ko="false"
diff --git a/meson.build b/meson.build
index e86085b0a4..e98cfa4909 100644
--- a/meson.build
+++ b/meson.build
@@ -264,6 +264,7 @@ libgit_sources = [
'compat/nonblock.c',
'compat/obstack.c',
'compat/terminal.c',
+ 'compiler-tricks/not-constant.c',
'config.c',
'connect.c',
'connected.c',
@@ -540,6 +541,7 @@ builtin_sources = [
'builtin/diagnose.c',
'builtin/diff-files.c',
'builtin/diff-index.c',
+ 'builtin/diff-pairs.c',
'builtin/diff-tree.c',
'builtin/diff.c',
'builtin/difftool.c',
@@ -581,7 +583,6 @@ builtin_sources = [
'builtin/name-rev.c',
'builtin/notes.c',
'builtin/pack-objects.c',
- 'builtin/pack-redundant.c',
'builtin/pack-refs.c',
'builtin/patch-id.c',
'builtin/prune-packed.c',
@@ -632,6 +633,10 @@ builtin_sources = [
'builtin/write-tree.c',
]
+if not get_option('breaking_changes')
+ builtin_sources += 'builtin/pack-redundant.c'
+endif
+
builtin_sources += custom_target(
output: 'config-list.h',
command: [
@@ -672,12 +677,6 @@ build_options_config.set_quoted('GIT_TEST_UTF8_LOCALE', get_option('test_utf8_lo
build_options_config.set_quoted('LOCALEDIR', fs.as_posix(get_option('prefix') / get_option('localedir')))
build_options_config.set('GITWEBDIR', fs.as_posix(get_option('prefix') / get_option('datadir') / 'gitweb'))
-if get_option('breaking_changes')
- build_options_config.set('WITH_BREAKING_CHANGES', 'YesPlease')
-else
- build_options_config.set('WITH_BREAKING_CHANGES', '')
-endif
-
if get_option('sane_tool_path').length() != 0
sane_tool_path = (host_machine.system() == 'windows' ? ';' : ':').join(get_option('sane_tool_path'))
build_options_config.set_quoted('BROKEN_PATH_FIX', 's|^\# @BROKEN_PATH_FIX@$|git_broken_path_fix "' + sane_tool_path + '"|')
@@ -721,6 +720,7 @@ if get_option('warning_level') in ['2','3', 'everything'] and compiler.get_argum
'-Woverflow',
'-Wpointer-arith',
'-Wstrict-prototypes',
+ '-Wunreachable-code',
'-Wunused',
'-Wvla',
'-Wwrite-strings',
@@ -739,6 +739,13 @@ if get_option('warning_level') in ['2','3', 'everything'] and compiler.get_argum
endforeach
endif
+if get_option('breaking_changes')
+ build_options_config.set('WITH_BREAKING_CHANGES', 'YesPlease')
+ libgit_c_args += '-DWITH_BREAKING_CHANGES'
+else
+ build_options_config.set('WITH_BREAKING_CHANGES', '')
+endif
+
if get_option('b_sanitize').contains('address')
build_options_config.set('SANITIZE_ADDRESS', 'YesCompiledWithIt')
else
@@ -772,7 +779,7 @@ endif
# features. It is optional if you want to neither execute tests nor use any of
# these optional features.
perl_required = get_option('perl')
-if get_option('tests') or get_option('gitweb').enabled() or 'netrc' in get_option('credential_helpers')
+if get_option('tests') or get_option('gitweb').enabled() or 'netrc' in get_option('credential_helpers') or get_option('docs') != []
perl_required = true
endif
@@ -1109,11 +1116,11 @@ elif host_machine.system() == 'windows'
libgit_sources += [
'compat/mingw.c',
'compat/winansi.c',
+ 'compat/win32/dirent.c',
'compat/win32/flush.c',
'compat/win32/path-utils.c',
'compat/win32/pthread.c',
'compat/win32/syslog.c',
- 'compat/win32/dirent.c',
'compat/win32mmap.c',
'compat/nedmalloc/nedmalloc.c',
]
diff --git a/midx-write.c b/midx-write.c
index 61b59d557d..0897cbd829 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -647,16 +647,22 @@ static uint32_t *midx_pack_order(struct write_midx_context *ctx)
return pack_order;
}
-static void write_midx_reverse_index(char *midx_name, unsigned char *midx_hash,
- struct write_midx_context *ctx)
+static void write_midx_reverse_index(struct write_midx_context *ctx,
+ const char *object_dir,
+ unsigned char *midx_hash)
{
struct strbuf buf = STRBUF_INIT;
char *tmp_file;
trace2_region_enter("midx", "write_midx_reverse_index", ctx->repo);
- strbuf_addf(&buf, "%s-%s.rev", midx_name, hash_to_hex_algop(midx_hash,
- ctx->repo->hash_algo));
+ if (ctx->incremental)
+ get_split_midx_filename_ext(ctx->repo->hash_algo, &buf,
+ object_dir, midx_hash,
+ MIDX_EXT_REV);
+ else
+ get_midx_filename_ext(ctx->repo->hash_algo, &buf, object_dir,
+ midx_hash, MIDX_EXT_REV);
tmp_file = write_rev_file_order(ctx->repo->hash_algo, NULL, ctx->pack_order,
ctx->entries_nr, midx_hash, WRITE_REV);
@@ -829,22 +835,29 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
return cb.commits;
}
-static int write_midx_bitmap(struct repository *r, const char *midx_name,
+static int write_midx_bitmap(struct write_midx_context *ctx,
+ const char *object_dir,
const unsigned char *midx_hash,
struct packing_data *pdata,
struct commit **commits,
uint32_t commits_nr,
- uint32_t *pack_order,
unsigned flags)
{
int ret, i;
uint16_t options = 0;
struct bitmap_writer writer;
struct pack_idx_entry **index;
- char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name,
- hash_to_hex_algop(midx_hash, r->hash_algo));
+ struct strbuf bitmap_name = STRBUF_INIT;
+
+ trace2_region_enter("midx", "write_midx_bitmap", ctx->repo);
- trace2_region_enter("midx", "write_midx_bitmap", r);
+ if (ctx->incremental)
+ get_split_midx_filename_ext(ctx->repo->hash_algo, &bitmap_name,
+ object_dir, midx_hash,
+ MIDX_EXT_BITMAP);
+ else
+ get_midx_filename_ext(ctx->repo->hash_algo, &bitmap_name,
+ object_dir, midx_hash, MIDX_EXT_BITMAP);
if (flags & MIDX_WRITE_BITMAP_HASH_CACHE)
options |= BITMAP_OPT_HASH_CACHE;
@@ -861,7 +874,8 @@ static int write_midx_bitmap(struct repository *r, const char *midx_name,
for (i = 0; i < pdata->nr_objects; i++)
index[i] = &pdata->objects[i].idx;
- bitmap_writer_init(&writer, r, pdata);
+ bitmap_writer_init(&writer, ctx->repo, pdata,
+ ctx->incremental ? ctx->base_midx : NULL);
bitmap_writer_show_progress(&writer, flags & MIDX_PROGRESS);
bitmap_writer_build_type_index(&writer, index);
@@ -879,7 +893,7 @@ static int write_midx_bitmap(struct repository *r, const char *midx_name,
* bitmap_writer_finish().
*/
for (i = 0; i < pdata->nr_objects; i++)
- index[pack_order[i]] = &pdata->objects[i].idx;
+ index[ctx->pack_order[i]] = &pdata->objects[i].idx;
bitmap_writer_select_commits(&writer, commits, commits_nr);
ret = bitmap_writer_build(&writer);
@@ -887,14 +901,14 @@ static int write_midx_bitmap(struct repository *r, const char *midx_name,
goto cleanup;
bitmap_writer_set_checksum(&writer, midx_hash);
- bitmap_writer_finish(&writer, index, bitmap_name, options);
+ bitmap_writer_finish(&writer, index, bitmap_name.buf, options);
cleanup:
free(index);
- free(bitmap_name);
+ strbuf_release(&bitmap_name);
bitmap_writer_free(&writer);
- trace2_region_leave("midx", "write_midx_bitmap", r);
+ trace2_region_leave("midx", "write_midx_bitmap", ctx->repo);
return ret;
}
@@ -1077,8 +1091,6 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
ctx.repo = r;
ctx.incremental = !!(flags & MIDX_WRITE_INCREMENTAL);
- if (ctx.incremental && (flags & MIDX_WRITE_BITMAP))
- die(_("cannot write incremental MIDX with bitmap"));
if (ctx.incremental)
strbuf_addf(&midx_name,
@@ -1119,6 +1131,13 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
if (ctx.incremental) {
struct multi_pack_index *m = ctx.base_midx;
while (m) {
+ if (flags & MIDX_WRITE_BITMAP && load_midx_revindex(m)) {
+ error(_("could not load reverse index for MIDX %s"),
+ hash_to_hex_algop(get_midx_checksum(m),
+ m->repo->hash_algo));
+ result = 1;
+ goto cleanup;
+ }
ctx.num_multi_pack_indexes_before++;
m = m->base_midx;
}
@@ -1336,7 +1355,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
return -1;
}
- if (adjust_shared_perm(get_tempfile_path(incr))) {
+ if (adjust_shared_perm(r, get_tempfile_path(incr))) {
error(_("unable to adjust shared permissions for '%s'"),
get_tempfile_path(incr));
return -1;
@@ -1387,7 +1406,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
if (flags & MIDX_WRITE_REV_INDEX &&
git_env_bool("GIT_TEST_MIDX_WRITE_REV", 0))
- write_midx_reverse_index(midx_name.buf, midx_hash, &ctx);
+ write_midx_reverse_index(&ctx, object_dir, midx_hash);
if (flags & MIDX_WRITE_BITMAP) {
struct packing_data pdata;
@@ -1410,8 +1429,8 @@ static int write_midx_internal(struct repository *r, const char *object_dir,
FREE_AND_NULL(ctx.entries);
ctx.entries_nr = 0;
- if (write_midx_bitmap(r, midx_name.buf, midx_hash, &pdata,
- commits, commits_nr, ctx.pack_order,
+ if (write_midx_bitmap(&ctx, object_dir,
+ midx_hash, &pdata, commits, commits_nr,
flags) < 0) {
error(_("could not write multi-pack bitmap"));
result = 1;
diff --git a/notes-merge.c b/notes-merge.c
index 8d701ed428..67a472020d 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -275,41 +275,45 @@ static void diff_tree_local(struct notes_merge_options *o,
static void check_notes_merge_worktree(struct notes_merge_options *o)
{
+ struct strbuf buf = STRBUF_INIT;
+
if (!o->has_worktree) {
/*
* Must establish NOTES_MERGE_WORKTREE.
* Abort if NOTES_MERGE_WORKTREE already exists
*/
- if (file_exists(git_path(NOTES_MERGE_WORKTREE)) &&
- !is_empty_dir(git_path(NOTES_MERGE_WORKTREE))) {
+ if (file_exists(repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE)) &&
+ !is_empty_dir(repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE))) {
if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
die(_("You have not concluded your previous "
"notes merge (%s exists).\nPlease, use "
"'git notes merge --commit' or 'git notes "
"merge --abort' to commit/abort the "
"previous merge before you start a new "
- "notes merge."), git_path("NOTES_MERGE_*"));
+ "notes merge."), repo_git_path_replace(the_repository, &buf, "NOTES_MERGE_*"));
else
die(_("You have not concluded your notes merge "
- "(%s exists)."), git_path("NOTES_MERGE_*"));
+ "(%s exists)."), repo_git_path_replace(the_repository, &buf, "NOTES_MERGE_*"));
}
- if (safe_create_leading_directories_const(git_path(
+ if (safe_create_leading_directories_const(repo_git_path_replace(the_repository, &buf,
NOTES_MERGE_WORKTREE "/.test")))
die_errno("unable to create directory %s",
- git_path(NOTES_MERGE_WORKTREE));
+ repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE));
o->has_worktree = 1;
- } else if (!file_exists(git_path(NOTES_MERGE_WORKTREE)))
+ } else if (!file_exists(repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE)))
/* NOTES_MERGE_WORKTREE should already be established */
die("missing '%s'. This should not happen",
- git_path(NOTES_MERGE_WORKTREE));
+ repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE));
+
+ strbuf_release(&buf);
}
static void write_buf_to_worktree(const struct object_id *obj,
const char *buf, unsigned long size)
{
int fd;
- char *path = git_pathdup(NOTES_MERGE_WORKTREE "/%s", oid_to_hex(obj));
+ char *path = repo_git_path(the_repository, NOTES_MERGE_WORKTREE "/%s", oid_to_hex(obj));
if (safe_create_leading_directories_const(path))
die_errno("unable to create directory for '%s'", path);
@@ -695,7 +699,7 @@ int notes_merge_commit(struct notes_merge_options *o,
const char *msg = strstr(buffer, "\n\n");
int baselen;
- git_path_buf(&path, NOTES_MERGE_WORKTREE);
+ repo_git_path_replace(the_repository, &path, NOTES_MERGE_WORKTREE);
if (o->verbosity >= 3)
printf("Committing notes in notes merge worktree at %s\n",
path.buf);
@@ -757,7 +761,7 @@ int notes_merge_abort(struct notes_merge_options *o)
struct strbuf buf = STRBUF_INIT;
int ret;
- git_path_buf(&buf, NOTES_MERGE_WORKTREE);
+ repo_git_path_replace(the_repository, &buf, NOTES_MERGE_WORKTREE);
if (o->verbosity >= 3)
printf("Removing notes merge worktree at %s/*\n", buf.buf);
ret = remove_dir_recursively(&buf, REMOVE_DIR_KEEP_TOPLEVEL);
diff --git a/object-file.c b/object-file.c
index 00c3a4b910..8da62fed0b 100644
--- a/object-file.c
+++ b/object-file.c
@@ -394,7 +394,7 @@ int mkdir_in_gitdir(const char *path)
}
strbuf_release(&sb);
}
- return adjust_shared_perm(path);
+ return adjust_shared_perm(the_repository, path);
}
static enum scld_error safe_create_leading_directories_1(char *path, int share)
@@ -443,7 +443,7 @@ static enum scld_error safe_create_leading_directories_1(char *path, int share)
ret = SCLD_VANISHED;
else
ret = SCLD_FAILED;
- } else if (share && adjust_shared_perm(path)) {
+ } else if (share && adjust_shared_perm(the_repository, path)) {
ret = SCLD_PERMS;
}
*slash = slash_character;
@@ -482,14 +482,14 @@ int odb_mkstemp(struct strbuf *temp_filename, const char *pattern)
* restrictive except to remove write permission.
*/
int mode = 0444;
- git_path_buf(temp_filename, "objects/%s", pattern);
+ repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern);
fd = git_mkstemp_mode(temp_filename->buf, mode);
if (0 <= fd)
return fd;
/* slow path */
/* some mkstemp implementations erase temp_filename on failure */
- git_path_buf(temp_filename, "objects/%s", pattern);
+ repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern);
safe_create_leading_directories(temp_filename->buf);
return xmkstemp_mode(temp_filename->buf, mode);
}
@@ -723,7 +723,7 @@ static void read_info_alternates(struct repository *r,
void add_to_alternates_file(const char *reference)
{
struct lock_file lock = LOCK_INIT;
- char *alts = git_pathdup("objects/info/alternates");
+ char *alts = repo_git_path(the_repository, "objects/info/alternates");
FILE *in, *out;
int found = 0;
@@ -1362,7 +1362,7 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
obj_read_unlock();
status = git_inflate(stream, 0);
obj_read_lock();
- if (status < Z_OK)
+ if (status != Z_OK && status != Z_STREAM_END)
return ULHR_BAD;
/*
@@ -1385,20 +1385,19 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
* reading the stream.
*/
strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
- stream->next_out = buffer;
- stream->avail_out = bufsiz;
do {
+ stream->next_out = buffer;
+ stream->avail_out = bufsiz;
+
obj_read_unlock();
status = git_inflate(stream, 0);
obj_read_lock();
strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
return 0;
- stream->next_out = buffer;
- stream->avail_out = bufsiz;
- } while (status != Z_STREAM_END);
- return ULHR_TOO_LONG;
+ } while (status == Z_OK);
+ return ULHR_BAD;
}
static void *unpack_loose_rest(git_zstream *stream,
@@ -1437,18 +1436,17 @@ static void *unpack_loose_rest(git_zstream *stream,
obj_read_lock();
}
}
- if (status == Z_STREAM_END && !stream->avail_in) {
- git_inflate_end(stream);
- return buf;
- }
- if (status < 0)
+ if (status != Z_STREAM_END) {
error(_("corrupt loose object '%s'"), oid_to_hex(oid));
- else if (stream->avail_in)
+ FREE_AND_NULL(buf);
+ } else if (stream->avail_in) {
error(_("garbage at end of loose object '%s'"),
oid_to_hex(oid));
- free(buf);
- return NULL;
+ FREE_AND_NULL(buf);
+ }
+
+ return buf;
}
/*
@@ -1580,6 +1578,8 @@ static int loose_object_info(struct repository *r,
if (!oi->contentp)
break;
+ if (hdrbuf.len)
+ BUG("unpacking content with unknown types not yet supported");
*oi->contentp = unpack_loose_rest(&stream, hdr, *oi->sizep, oid);
if (*oi->contentp)
goto cleanup;
@@ -1600,8 +1600,8 @@ static int loose_object_info(struct repository *r,
die(_("loose object %s (stored in %s) is corrupt"),
oid_to_hex(oid), path);
- git_inflate_end(&stream);
cleanup:
+ git_inflate_end(&stream);
munmap(map, mapsize);
if (oi->sizep == &size_scratch)
oi->sizep = NULL;
@@ -2111,7 +2111,7 @@ retry:
}
out:
- if (adjust_shared_perm(filename))
+ if (adjust_shared_perm(the_repository, filename))
return error(_("unable to set permission to '%s'"), filename);
return 0;
}
@@ -2187,7 +2187,7 @@ static int create_tmpfile(struct strbuf *tmp, const char *filename)
strbuf_add(tmp, filename, dirlen - 1);
if (mkdir(tmp->buf, 0777) && errno != EEXIST)
return -1;
- if (adjust_shared_perm(tmp->buf))
+ if (adjust_shared_perm(the_repository, tmp->buf))
return -1;
/* Try again */
@@ -2706,7 +2706,7 @@ static int index_stream_convert_blob(struct index_state *istate,
struct strbuf sbuf = STRBUF_INIT;
assert(path);
- assert(would_convert_to_git_filter_fd(istate, path));
+ ASSERT(would_convert_to_git_filter_fd(istate, path));
convert_to_git_filter_fd(istate, path, fd, &sbuf,
get_conv_flags(flags));
@@ -3080,7 +3080,6 @@ static int check_stream_oid(git_zstream *stream,
git_hash_update(&c, buf, stream->next_out - buf);
total_read += stream->next_out - buf;
}
- git_inflate_end(stream);
if (status != Z_STREAM_END) {
error(_("corrupt loose object '%s'"), oid_to_hex(expected_oid));
@@ -3127,35 +3126,34 @@ int read_loose_object(const char *path,
if (unpack_loose_header(&stream, map, mapsize, hdr, sizeof(hdr),
NULL) != ULHR_OK) {
error(_("unable to unpack header of %s"), path);
- git_inflate_end(&stream);
- goto out;
+ goto out_inflate;
}
if (parse_loose_header(hdr, oi) < 0) {
error(_("unable to parse header of %s"), path);
- git_inflate_end(&stream);
- goto out;
+ goto out_inflate;
}
if (*oi->typep == OBJ_BLOB && *size > big_file_threshold) {
if (check_stream_oid(&stream, hdr, *size, path, expected_oid) < 0)
- goto out;
+ goto out_inflate;
} else {
*contents = unpack_loose_rest(&stream, hdr, *size, expected_oid);
if (!*contents) {
error(_("unable to unpack contents of %s"), path);
- git_inflate_end(&stream);
- goto out;
+ goto out_inflate;
}
hash_object_file_literally(the_repository->hash_algo,
*contents, *size,
oi->type_name->buf, real_oid);
if (!oideq(expected_oid, real_oid))
- goto out;
+ goto out_inflate;
}
ret = 0; /* everything checks out */
+out_inflate:
+ git_inflate_end(&stream);
out:
if (map)
munmap(map, mapsize);
diff --git a/object-name.c b/object-name.c
index 945d5bdef2..91f731373a 100644
--- a/object-name.c
+++ b/object-name.c
@@ -961,7 +961,9 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
int fatal = !(flags & GET_OID_QUIETLY);
if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) {
- if (repo_settings_get_warn_ambiguous_refs(r) && warn_on_object_refname_ambiguity) {
+ if (!(flags & GET_OID_SKIP_AMBIGUITY_CHECK) &&
+ repo_settings_get_warn_ambiguous_refs(r) &&
+ warn_on_object_refname_ambiguity) {
refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref, 0);
if (refs_found > 0) {
warning(warn_msg, len, str);
@@ -1273,7 +1275,7 @@ static int peel_onion(struct repository *r, const char *name, int len,
}
/*
- * Documentation/revisions.txt says:
+ * Documentation/revisions.adoc says:
* '<describeOutput>', e.g. 'v1.7.4.2-679-g3bee7fb'::
* Output from `git describe`; i.e. a closest tag, optionally
* followed by a dash and a number of commits, followed by a dash, a
@@ -1794,18 +1796,20 @@ void object_context_release(struct object_context *ctx)
strbuf_release(&ctx->symlink_path);
}
-/*
- * This is like "get_oid_basic()", except it allows "object ID expressions",
- * notably "xyz^" for "parent of xyz"
- */
-int repo_get_oid(struct repository *r, const char *name, struct object_id *oid)
+int repo_get_oid_with_flags(struct repository *r, const char *name,
+ struct object_id *oid, unsigned flags)
{
struct object_context unused;
- int ret = get_oid_with_context(r, name, 0, oid, &unused);
+ int ret = get_oid_with_context(r, name, flags, oid, &unused);
object_context_release(&unused);
return ret;
}
+int repo_get_oid(struct repository *r, const char *name, struct object_id *oid)
+{
+ return repo_get_oid_with_flags(r, name, oid, 0);
+}
+
/*
* This returns a non-zero value if the string (built using printf
* format and the given arguments) is not a valid object.
diff --git a/object-name.h b/object-name.h
index 8dba4a47a4..cda4934cd5 100644
--- a/object-name.h
+++ b/object-name.h
@@ -51,6 +51,12 @@ void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo,
void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
int abbrev_len);
+/*
+ * This is like "get_oid_basic()", except it allows "object ID expressions",
+ * notably "xyz^" for "parent of xyz". Accepts GET_OID_* flags.
+ */
+int repo_get_oid_with_flags(struct repository *r, const char *str,
+ struct object_id *oid, unsigned flags);
int repo_get_oid(struct repository *r, const char *str, struct object_id *oid);
__attribute__((format (printf, 2, 3)))
int get_oidf(struct object_id *oid, const char *fmt, ...);
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index a06a1f35c6..8a30853d2e 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -26,6 +26,8 @@
#include "alloc.h"
#include "refs.h"
#include "strmap.h"
+#include "midx.h"
+#include "pack-revindex.h"
struct bitmapped_commit {
struct commit *commit;
@@ -43,7 +45,8 @@ static inline int bitmap_writer_nr_selected_commits(struct bitmap_writer *writer
}
void bitmap_writer_init(struct bitmap_writer *writer, struct repository *r,
- struct packing_data *pdata)
+ struct packing_data *pdata,
+ struct multi_pack_index *midx)
{
memset(writer, 0, sizeof(struct bitmap_writer));
if (writer->bitmaps)
@@ -51,6 +54,7 @@ void bitmap_writer_init(struct bitmap_writer *writer, struct repository *r,
writer->bitmaps = kh_init_oid_map();
writer->pseudo_merge_commits = kh_init_oid_map();
writer->to_pack = pdata;
+ writer->midx = midx;
string_list_init_dup(&writer->pseudo_merge_groups);
@@ -113,6 +117,11 @@ void bitmap_writer_build_type_index(struct bitmap_writer *writer,
struct pack_idx_entry **index)
{
uint32_t i;
+ uint32_t base_objects = 0;
+
+ if (writer->midx)
+ base_objects = writer->midx->num_objects +
+ writer->midx->num_objects_in_base;
writer->commits = ewah_new();
writer->trees = ewah_new();
@@ -142,19 +151,19 @@ void bitmap_writer_build_type_index(struct bitmap_writer *writer,
switch (real_type) {
case OBJ_COMMIT:
- ewah_set(writer->commits, i);
+ ewah_set(writer->commits, i + base_objects);
break;
case OBJ_TREE:
- ewah_set(writer->trees, i);
+ ewah_set(writer->trees, i + base_objects);
break;
case OBJ_BLOB:
- ewah_set(writer->blobs, i);
+ ewah_set(writer->blobs, i + base_objects);
break;
case OBJ_TAG:
- ewah_set(writer->tags, i);
+ ewah_set(writer->tags, i + base_objects);
break;
default:
@@ -207,19 +216,37 @@ void bitmap_writer_push_commit(struct bitmap_writer *writer,
static uint32_t find_object_pos(struct bitmap_writer *writer,
const struct object_id *oid, int *found)
{
- struct object_entry *entry = packlist_find(writer->to_pack, oid);
+ struct object_entry *entry;
+
+ entry = packlist_find(writer->to_pack, oid);
+ if (entry) {
+ uint32_t base_objects = 0;
+ if (writer->midx)
+ base_objects = writer->midx->num_objects +
+ writer->midx->num_objects_in_base;
- if (!entry) {
if (found)
- *found = 0;
- warning("Failed to write bitmap index. Packfile doesn't have full closure "
- "(object %s is missing)", oid_to_hex(oid));
- return 0;
+ *found = 1;
+ return oe_in_pack_pos(writer->to_pack, entry) + base_objects;
+ } else if (writer->midx) {
+ uint32_t at, pos;
+
+ if (!bsearch_midx(oid, writer->midx, &at))
+ goto missing;
+ if (midx_to_pack_pos(writer->midx, at, &pos) < 0)
+ goto missing;
+
+ if (found)
+ *found = 1;
+ return pos;
}
+missing:
if (found)
- *found = 1;
- return oe_in_pack_pos(writer->to_pack, entry);
+ *found = 0;
+ warning("Failed to write bitmap index. Packfile doesn't have full closure "
+ "(object %s is missing)", oid_to_hex(oid));
+ return 0;
}
static void compute_xor_offsets(struct bitmap_writer *writer)
@@ -586,7 +613,7 @@ int bitmap_writer_build(struct bitmap_writer *writer)
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
struct prio_queue tree_queue = { NULL };
struct bitmap_index *old_bitmap;
- uint32_t *mapping;
+ uint32_t *mapping = NULL;
int closed = 1; /* until proven otherwise */
if (writer->show_progress)
@@ -1021,7 +1048,7 @@ void bitmap_writer_finish(struct bitmap_writer *writer,
struct strbuf tmp_file = STRBUF_INIT;
struct hashfile *f;
off_t *offsets = NULL;
- uint32_t i;
+ uint32_t i, base_objects;
struct bitmap_disk_header header;
@@ -1047,6 +1074,12 @@ void bitmap_writer_finish(struct bitmap_writer *writer,
if (options & BITMAP_OPT_LOOKUP_TABLE)
CALLOC_ARRAY(offsets, writer->to_pack->nr_objects);
+ if (writer->midx)
+ base_objects = writer->midx->num_objects +
+ writer->midx->num_objects_in_base;
+ else
+ base_objects = 0;
+
for (i = 0; i < bitmap_writer_nr_selected_commits(writer); i++) {
struct bitmapped_commit *stored = &writer->selected[i];
int commit_pos = oid_pos(&stored->commit->object.oid, index,
@@ -1055,7 +1088,7 @@ void bitmap_writer_finish(struct bitmap_writer *writer,
if (commit_pos < 0)
BUG(_("trying to write commit not in index"));
- stored->commit_pos = commit_pos;
+ stored->commit_pos = commit_pos + base_objects;
}
write_selected_commits_v1(writer, f, offsets);
@@ -1072,7 +1105,7 @@ void bitmap_writer_finish(struct bitmap_writer *writer,
finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
- if (adjust_shared_perm(tmp_file.buf))
+ if (adjust_shared_perm(the_repository, tmp_file.buf))
die_errno("unable to make temporary bitmap file readable");
if (rename(tmp_file.buf, filename))
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 6406953d32..6f7fd94c36 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -54,6 +54,16 @@ struct bitmap_index {
struct packed_git *pack;
struct multi_pack_index *midx;
+ /*
+ * If using a multi-pack index chain, 'base' points to the
+ * bitmap index corresponding to this bitmap's midx->base_midx.
+ *
+ * base_nr indicates how many layers precede this one, and is
+ * zero when base is NULL.
+ */
+ struct bitmap_index *base;
+ uint32_t base_nr;
+
/* mmapped buffer of the whole bitmap index */
unsigned char *map;
size_t map_size; /* size of the mmaped buffer */
@@ -71,6 +81,23 @@ struct bitmap_index {
struct ewah_bitmap *blobs;
struct ewah_bitmap *tags;
+ /*
+ * Type index arrays when this bitmap is associated with an
+ * incremental multi-pack index chain.
+ *
+ * If n is the number of unique layers in the MIDX chain, then
+ * commits_all[n-1] is this structs 'commits' field,
+ * commits_all[n-2] is the commits field of this bitmap's
+ * 'base', and so on.
+ *
+ * When associated either with a non-incremental MIDX or a
+ * single packfile, these arrays each contain a single element.
+ */
+ struct ewah_bitmap **commits_all;
+ struct ewah_bitmap **trees_all;
+ struct ewah_bitmap **blobs_all;
+ struct ewah_bitmap **tags_all;
+
/* Map from object ID -> `stored_bitmap` for all the bitmapped commits */
kh_oid_map_t *bitmaps;
@@ -170,6 +197,15 @@ static struct ewah_bitmap *read_bitmap_1(struct bitmap_index *index)
return read_bitmap(index->map, index->map_size, &index->map_pos);
}
+static uint32_t bitmap_num_objects_total(struct bitmap_index *index)
+{
+ if (index->midx) {
+ struct multi_pack_index *m = index->midx;
+ return m->num_objects + m->num_objects_in_base;
+ }
+ return index->pack->num_objects;
+}
+
static uint32_t bitmap_num_objects(struct bitmap_index *index)
{
if (index->midx)
@@ -377,8 +413,15 @@ static int load_bitmap_entries_v1(struct bitmap_index *index)
char *midx_bitmap_filename(struct multi_pack_index *midx)
{
struct strbuf buf = STRBUF_INIT;
- get_midx_filename_ext(midx->repo->hash_algo, &buf, midx->object_dir,
- get_midx_checksum(midx), MIDX_EXT_BITMAP);
+ if (midx->has_chain)
+ get_split_midx_filename_ext(midx->repo->hash_algo, &buf,
+ midx->object_dir,
+ get_midx_checksum(midx),
+ MIDX_EXT_BITMAP);
+ else
+ get_midx_filename_ext(midx->repo->hash_algo, &buf,
+ midx->object_dir, get_midx_checksum(midx),
+ MIDX_EXT_BITMAP);
return strbuf_detach(&buf, NULL);
}
@@ -445,16 +488,21 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git,
goto cleanup;
}
- for (i = 0; i < bitmap_git->midx->num_packs; i++) {
- if (prepare_midx_pack(bitmap_repo(bitmap_git),
- bitmap_git->midx,
- i)) {
+ for (i = 0; i < bitmap_git->midx->num_packs + bitmap_git->midx->num_packs_in_base; i++) {
+ if (prepare_midx_pack(bitmap_repo(bitmap_git), bitmap_git->midx, i)) {
warning(_("could not open pack %s"),
bitmap_git->midx->pack_names[i]);
goto cleanup;
}
}
+ if (midx->base_midx) {
+ bitmap_git->base = prepare_midx_bitmap_git(midx->base_midx);
+ bitmap_git->base_nr = bitmap_git->base->base_nr + 1;
+ } else {
+ bitmap_git->base_nr = 0;
+ }
+
return 0;
cleanup:
@@ -506,6 +554,7 @@ static int open_pack_bitmap_1(struct bitmap_index *bitmap_git, struct packed_git
bitmap_git->map_size = xsize_t(st.st_size);
bitmap_git->map = xmmap(NULL, bitmap_git->map_size, PROT_READ, MAP_PRIVATE, fd, 0);
bitmap_git->map_pos = 0;
+ bitmap_git->base_nr = 0;
close(fd);
if (load_bitmap_header(bitmap_git) < 0) {
@@ -525,8 +574,7 @@ static int open_pack_bitmap_1(struct bitmap_index *bitmap_git, struct packed_git
static int load_reverse_index(struct repository *r, struct bitmap_index *bitmap_git)
{
if (bitmap_is_midx(bitmap_git)) {
- uint32_t i;
- int ret;
+ struct multi_pack_index *m;
/*
* The multi-pack-index's .rev file is already loaded via
@@ -535,17 +583,47 @@ static int load_reverse_index(struct repository *r, struct bitmap_index *bitmap_
* But we still need to open the individual pack .rev files,
* since we will need to make use of them in pack-objects.
*/
- for (i = 0; i < bitmap_git->midx->num_packs; i++) {
- ret = load_pack_revindex(r, bitmap_git->midx->packs[i]);
- if (ret)
- return ret;
+ for (m = bitmap_git->midx; m; m = m->base_midx) {
+ uint32_t i;
+ int ret;
+
+ for (i = 0; i < m->num_packs; i++) {
+ ret = load_pack_revindex(r, m->packs[i]);
+ if (ret)
+ return ret;
+ }
}
return 0;
}
return load_pack_revindex(r, bitmap_git->pack);
}
-static int load_bitmap(struct repository *r, struct bitmap_index *bitmap_git)
+static void load_all_type_bitmaps(struct bitmap_index *bitmap_git)
+{
+ struct bitmap_index *curr = bitmap_git;
+ size_t i = bitmap_git->base_nr;
+
+ ALLOC_ARRAY(bitmap_git->commits_all, bitmap_git->base_nr + 1);
+ ALLOC_ARRAY(bitmap_git->trees_all, bitmap_git->base_nr + 1);
+ ALLOC_ARRAY(bitmap_git->blobs_all, bitmap_git->base_nr + 1);
+ ALLOC_ARRAY(bitmap_git->tags_all, bitmap_git->base_nr + 1);
+
+ while (curr) {
+ bitmap_git->commits_all[i] = curr->commits;
+ bitmap_git->trees_all[i] = curr->trees;
+ bitmap_git->blobs_all[i] = curr->blobs;
+ bitmap_git->tags_all[i] = curr->tags;
+
+ curr = curr->base;
+ if (curr && !i)
+ BUG("unexpected number of bitmap layers, expected %"PRIu32,
+ bitmap_git->base_nr + 1);
+ i -= 1;
+ }
+}
+
+static int load_bitmap(struct repository *r, struct bitmap_index *bitmap_git,
+ int recursing)
{
assert(bitmap_git->map);
@@ -564,6 +642,16 @@ static int load_bitmap(struct repository *r, struct bitmap_index *bitmap_git)
if (!bitmap_git->table_lookup && load_bitmap_entries_v1(bitmap_git) < 0)
goto failed;
+ if (bitmap_git->base) {
+ if (!bitmap_is_midx(bitmap_git))
+ BUG("non-MIDX bitmap has non-NULL base bitmap index");
+ if (load_bitmap(r, bitmap_git->base, 1) < 0)
+ goto failed;
+ }
+
+ if (!recursing)
+ load_all_type_bitmaps(bitmap_git);
+
return 0;
failed:
@@ -639,7 +727,7 @@ struct bitmap_index *prepare_bitmap_git(struct repository *r)
{
struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
- if (!open_bitmap(r, bitmap_git) && !load_bitmap(r, bitmap_git))
+ if (!open_bitmap(r, bitmap_git) && !load_bitmap(r, bitmap_git, 0))
return bitmap_git;
free_bitmap_index(bitmap_git);
@@ -648,10 +736,9 @@ struct bitmap_index *prepare_bitmap_git(struct repository *r)
struct bitmap_index *prepare_midx_bitmap_git(struct multi_pack_index *midx)
{
- struct repository *r = midx->repo;
struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
- if (!open_midx_bitmap_1(bitmap_git, midx) && !load_bitmap(r, bitmap_git))
+ if (!open_midx_bitmap_1(bitmap_git, midx))
return bitmap_git;
free_bitmap_index(bitmap_git);
@@ -896,26 +983,42 @@ corrupt:
return NULL;
}
-struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,
- struct commit *commit)
+static struct ewah_bitmap *find_bitmap_for_commit(struct bitmap_index *bitmap_git,
+ struct commit *commit,
+ struct bitmap_index **found)
{
- khiter_t hash_pos = kh_get_oid_map(bitmap_git->bitmaps,
- commit->object.oid);
+ khiter_t hash_pos;
+ if (!bitmap_git)
+ return NULL;
+
+ hash_pos = kh_get_oid_map(bitmap_git->bitmaps, commit->object.oid);
if (hash_pos >= kh_end(bitmap_git->bitmaps)) {
struct stored_bitmap *bitmap = NULL;
if (!bitmap_git->table_lookup)
- return NULL;
+ return find_bitmap_for_commit(bitmap_git->base, commit,
+ found);
/* this is a fairly hot codepath - no trace2_region please */
/* NEEDSWORK: cache misses aren't recorded */
bitmap = lazy_bitmap_for_commit(bitmap_git, commit);
if (!bitmap)
- return NULL;
+ return find_bitmap_for_commit(bitmap_git->base, commit,
+ found);
+ if (found)
+ *found = bitmap_git;
return lookup_stored_bitmap(bitmap);
}
+ if (found)
+ *found = bitmap_git;
return lookup_stored_bitmap(kh_value(bitmap_git->bitmaps, hash_pos));
}
+struct ewah_bitmap *bitmap_for_commit(struct bitmap_index *bitmap_git,
+ struct commit *commit)
+{
+ return find_bitmap_for_commit(bitmap_git, commit, NULL);
+}
+
static inline int bitmap_position_extended(struct bitmap_index *bitmap_git,
const struct object_id *oid)
{
@@ -924,7 +1027,7 @@ static inline int bitmap_position_extended(struct bitmap_index *bitmap_git,
if (pos < kh_end(positions)) {
int bitmap_pos = kh_value(positions, pos);
- return bitmap_pos + bitmap_num_objects(bitmap_git);
+ return bitmap_pos + bitmap_num_objects_total(bitmap_git);
}
return -1;
@@ -992,7 +1095,7 @@ static int ext_index_add_object(struct bitmap_index *bitmap_git,
bitmap_pos = kh_value(eindex->positions, hash_pos);
}
- return bitmap_pos + bitmap_num_objects(bitmap_git);
+ return bitmap_pos + bitmap_num_objects_total(bitmap_git);
}
struct bitmap_show_data {
@@ -1024,10 +1127,15 @@ static unsigned apply_pseudo_merges_for_commit_1(struct bitmap_index *bitmap_git
struct commit *commit,
uint32_t commit_pos)
{
- int ret;
+ struct bitmap_index *curr = bitmap_git;
+ int ret = 0;
- ret = apply_pseudo_merges_for_commit(&bitmap_git->pseudo_merges,
- result, commit, commit_pos);
+ while (curr) {
+ ret += apply_pseudo_merges_for_commit(&curr->pseudo_merges,
+ result, commit,
+ commit_pos);
+ curr = curr->base;
+ }
if (ret)
pseudo_merges_satisfied_nr += ret;
@@ -1342,11 +1450,17 @@ struct ewah_bitmap *pseudo_merge_bitmap_for_commit(struct bitmap_index *bitmap_g
if (pos < 0 || pos >= bitmap_num_objects(bitmap_git))
goto done;
+ /*
+ * Use bitmap-relative positions instead of offsetting
+ * by bitmap_git->num_objects_in_base because we use
+ * this to find a match in pseudo_merge_for_parents(),
+ * and pseudo-merge groups cannot span multiple bitmap
+ * layers.
+ */
bitmap_set(parents, pos);
}
- match = pseudo_merge_for_parents(&bitmap_git->pseudo_merges,
- parents);
+ match = pseudo_merge_for_parents(&bitmap_git->pseudo_merges, parents);
done:
bitmap_free(parents);
@@ -1500,7 +1614,9 @@ static void show_extended_objects(struct bitmap_index *bitmap_git,
for (i = 0; i < eindex->count; ++i) {
struct object *obj;
- if (!bitmap_get(objects, st_add(bitmap_num_objects(bitmap_git), i)))
+ if (!bitmap_get(objects,
+ st_add(bitmap_num_objects_total(bitmap_git),
+ i)))
continue;
obj = eindex->objects[i];
@@ -1513,25 +1629,29 @@ static void show_extended_objects(struct bitmap_index *bitmap_git,
}
}
-static void init_type_iterator(struct ewah_iterator *it,
+static void init_type_iterator(struct ewah_or_iterator *it,
struct bitmap_index *bitmap_git,
enum object_type type)
{
switch (type) {
case OBJ_COMMIT:
- ewah_iterator_init(it, bitmap_git->commits);
+ ewah_or_iterator_init(it, bitmap_git->commits_all,
+ bitmap_git->base_nr + 1);
break;
case OBJ_TREE:
- ewah_iterator_init(it, bitmap_git->trees);
+ ewah_or_iterator_init(it, bitmap_git->trees_all,
+ bitmap_git->base_nr + 1);
break;
case OBJ_BLOB:
- ewah_iterator_init(it, bitmap_git->blobs);
+ ewah_or_iterator_init(it, bitmap_git->blobs_all,
+ bitmap_git->base_nr + 1);
break;
case OBJ_TAG:
- ewah_iterator_init(it, bitmap_git->tags);
+ ewah_or_iterator_init(it, bitmap_git->tags_all,
+ bitmap_git->base_nr + 1);
break;
default:
@@ -1548,7 +1668,7 @@ static void show_objects_for_type(
size_t i = 0;
uint32_t offset;
- struct ewah_iterator it;
+ struct ewah_or_iterator it;
eword_t filter;
struct bitmap *objects = bitmap_git->result;
@@ -1556,7 +1676,7 @@ static void show_objects_for_type(
init_type_iterator(&it, bitmap_git, object_type);
for (i = 0; i < objects->word_alloc &&
- ewah_iterator_next(&filter, &it); i++) {
+ ewah_or_iterator_next(&filter, &it); i++) {
eword_t word = objects->words[i] & filter;
size_t pos = (i * BITS_IN_EWORD);
@@ -1583,7 +1703,7 @@ static void show_objects_for_type(
nth_midxed_object_oid(&oid, m, index_pos);
pack_id = nth_midxed_pack_int_id(m, index_pos);
- pack = bitmap_git->midx->packs[pack_id];
+ pack = nth_midxed_pack(bitmap_git->midx, pack_id);
} else {
index_pos = pack_pos_to_index(bitmap_git->pack, pos + offset);
ofs = pack_pos_to_offset(bitmap_git->pack, pos + offset);
@@ -1598,6 +1718,8 @@ static void show_objects_for_type(
show_reach(&oid, object_type, 0, hash, pack, ofs);
}
}
+
+ ewah_or_iterator_release(&it);
}
static int in_bitmapped_pack(struct bitmap_index *bitmap_git,
@@ -1649,7 +1771,7 @@ static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git,
{
struct eindex *eindex = &bitmap_git->ext_index;
struct bitmap *tips;
- struct ewah_iterator it;
+ struct ewah_or_iterator it;
eword_t mask;
uint32_t i;
@@ -1666,7 +1788,7 @@ static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git,
* packfile.
*/
for (i = 0, init_type_iterator(&it, bitmap_git, type);
- i < to_filter->word_alloc && ewah_iterator_next(&mask, &it);
+ i < to_filter->word_alloc && ewah_or_iterator_next(&mask, &it);
i++) {
if (i < tips->word_alloc)
mask &= ~tips->words[i];
@@ -1679,13 +1801,14 @@ static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git,
* them individually.
*/
for (i = 0; i < eindex->count; i++) {
- size_t pos = st_add(i, bitmap_num_objects(bitmap_git));
+ size_t pos = st_add(i, bitmap_num_objects_total(bitmap_git));
if (eindex->objects[i]->type == type &&
bitmap_get(to_filter, pos) &&
!bitmap_get(tips, pos))
bitmap_unset(to_filter, pos);
}
+ ewah_or_iterator_release(&it);
bitmap_free(tips);
}
@@ -1705,7 +1828,7 @@ static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git,
oi.sizep = &size;
- if (pos < bitmap_num_objects(bitmap_git)) {
+ if (pos < bitmap_num_objects_total(bitmap_git)) {
struct packed_git *pack;
off_t ofs;
@@ -1713,7 +1836,7 @@ static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git,
uint32_t midx_pos = pack_pos_to_midx(bitmap_git->midx, pos);
uint32_t pack_id = nth_midxed_pack_int_id(bitmap_git->midx, midx_pos);
- pack = bitmap_git->midx->packs[pack_id];
+ pack = nth_midxed_pack(bitmap_git->midx, pack_id);
ofs = nth_midxed_offset(bitmap_git->midx, midx_pos);
} else {
pack = bitmap_git->pack;
@@ -1728,8 +1851,9 @@ static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git,
die(_("unable to get size of %s"), oid_to_hex(&oid));
}
} else {
+ size_t eindex_pos = pos - bitmap_num_objects_total(bitmap_git);
struct eindex *eindex = &bitmap_git->ext_index;
- struct object *obj = eindex->objects[pos - bitmap_num_objects(bitmap_git)];
+ struct object *obj = eindex->objects[eindex_pos];
if (oid_object_info_extended(bitmap_repo(bitmap_git), &obj->oid,
&oi, 0) < 0)
die(_("unable to get size of %s"), oid_to_hex(&obj->oid));
@@ -1745,14 +1869,14 @@ static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git,
{
struct eindex *eindex = &bitmap_git->ext_index;
struct bitmap *tips;
- struct ewah_iterator it;
+ struct ewah_or_iterator it;
eword_t mask;
uint32_t i;
tips = find_tip_objects(bitmap_git, tip_objects, OBJ_BLOB);
for (i = 0, init_type_iterator(&it, bitmap_git, OBJ_BLOB);
- i < to_filter->word_alloc && ewah_iterator_next(&mask, &it);
+ i < to_filter->word_alloc && ewah_or_iterator_next(&mask, &it);
i++) {
eword_t word = to_filter->words[i] & mask;
unsigned offset;
@@ -1780,6 +1904,7 @@ static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git,
bitmap_unset(to_filter, pos);
}
+ ewah_or_iterator_release(&it);
bitmap_free(tips);
}
@@ -1882,7 +2007,7 @@ static void filter_packed_objects_from_bitmap(struct bitmap_index *bitmap_git,
uint32_t objects_nr;
size_t i, pos;
- objects_nr = bitmap_num_objects(bitmap_git);
+ objects_nr = bitmap_num_objects_total(bitmap_git);
pos = objects_nr / BITS_IN_EWORD;
if (pos > result->word_alloc)
@@ -1980,7 +2105,7 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
* from disk. this is the point of no return; after this the rev_list
* becomes invalidated and we must perform the revwalk through bitmaps
*/
- if (load_bitmap(revs->repo, bitmap_git) < 0)
+ if (load_bitmap(revs->repo, bitmap_git, 0) < 0)
goto cleanup;
if (!use_boundary_traversal)
@@ -2281,7 +2406,8 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
multi_pack_reuse = 0;
if (multi_pack_reuse) {
- for (i = 0; i < bitmap_git->midx->num_packs; i++) {
+ struct multi_pack_index *m = bitmap_git->midx;
+ for (i = 0; i < m->num_packs + m->num_packs_in_base; i++) {
struct bitmapped_pack pack;
if (nth_bitmapped_pack(r, bitmap_git->midx, &pack, i) < 0) {
warning(_("unable to load pack: '%s', disabling pack-reuse"),
@@ -2307,14 +2433,18 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
uint32_t pack_int_id;
if (bitmap_is_midx(bitmap_git)) {
+ struct multi_pack_index *m = bitmap_git->midx;
uint32_t preferred_pack_pos;
- if (midx_preferred_pack(bitmap_git->midx, &preferred_pack_pos) < 0) {
+ while (m->base_midx)
+ m = m->base_midx;
+
+ if (midx_preferred_pack(m, &preferred_pack_pos) < 0) {
warning(_("unable to compute preferred pack, disabling pack-reuse"));
return;
}
- pack = bitmap_git->midx->packs[preferred_pack_pos];
+ pack = nth_midxed_pack(m, preferred_pack_pos);
pack_int_id = preferred_pack_pos;
} else {
pack = bitmap_git->pack;
@@ -2406,12 +2536,12 @@ static uint32_t count_object_type(struct bitmap_index *bitmap_git,
struct eindex *eindex = &bitmap_git->ext_index;
uint32_t i = 0, count = 0;
- struct ewah_iterator it;
+ struct ewah_or_iterator it;
eword_t filter;
init_type_iterator(&it, bitmap_git, type);
- while (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) {
+ while (i < objects->word_alloc && ewah_or_iterator_next(&filter, &it)) {
eword_t word = objects->words[i++] & filter;
count += ewah_bit_popcount64(word);
}
@@ -2419,10 +2549,12 @@ static uint32_t count_object_type(struct bitmap_index *bitmap_git,
for (i = 0; i < eindex->count; ++i) {
if (eindex->objects[i]->type == type &&
bitmap_get(objects,
- st_add(bitmap_num_objects(bitmap_git), i)))
+ st_add(bitmap_num_objects_total(bitmap_git), i)))
count++;
}
+ ewah_or_iterator_release(&it);
+
return count;
}
@@ -2454,6 +2586,8 @@ struct bitmap_test_data {
struct bitmap *tags;
struct progress *prg;
size_t seen;
+
+ struct bitmap_test_data *base_tdata;
};
static void test_bitmap_type(struct bitmap_test_data *tdata,
@@ -2462,6 +2596,11 @@ static void test_bitmap_type(struct bitmap_test_data *tdata,
enum object_type bitmap_type = OBJ_NONE;
int bitmaps_nr = 0;
+ if (bitmap_is_midx(tdata->bitmap_git)) {
+ while (pos < tdata->bitmap_git->midx->num_objects_in_base)
+ tdata = tdata->base_tdata;
+ }
+
if (bitmap_get(tdata->commits, pos)) {
bitmap_type = OBJ_COMMIT;
bitmaps_nr++;
@@ -2525,13 +2664,57 @@ static void test_show_commit(struct commit *commit, void *data)
display_progress(tdata->prg, ++tdata->seen);
}
+static uint32_t bitmap_total_entry_count(struct bitmap_index *bitmap_git)
+{
+ uint32_t total = 0;
+ do {
+ total = st_add(total, bitmap_git->entry_count);
+ bitmap_git = bitmap_git->base;
+ } while (bitmap_git);
+
+ return total;
+}
+
+static void bitmap_test_data_prepare(struct bitmap_test_data *tdata,
+ struct bitmap_index *bitmap_git)
+{
+ memset(tdata, 0, sizeof(struct bitmap_test_data));
+
+ tdata->bitmap_git = bitmap_git;
+ tdata->base = bitmap_new();
+ tdata->commits = ewah_to_bitmap(bitmap_git->commits);
+ tdata->trees = ewah_to_bitmap(bitmap_git->trees);
+ tdata->blobs = ewah_to_bitmap(bitmap_git->blobs);
+ tdata->tags = ewah_to_bitmap(bitmap_git->tags);
+
+ if (bitmap_git->base) {
+ tdata->base_tdata = xmalloc(sizeof(struct bitmap_test_data));
+ bitmap_test_data_prepare(tdata->base_tdata, bitmap_git->base);
+ }
+}
+
+static void bitmap_test_data_release(struct bitmap_test_data *tdata)
+{
+ if (!tdata)
+ return;
+
+ bitmap_test_data_release(tdata->base_tdata);
+ free(tdata->base_tdata);
+
+ bitmap_free(tdata->base);
+ bitmap_free(tdata->commits);
+ bitmap_free(tdata->trees);
+ bitmap_free(tdata->blobs);
+ bitmap_free(tdata->tags);
+}
+
void test_bitmap_walk(struct rev_info *revs)
{
struct object *root;
struct bitmap *result = NULL;
size_t result_popcnt;
struct bitmap_test_data tdata;
- struct bitmap_index *bitmap_git;
+ struct bitmap_index *bitmap_git, *found;
struct ewah_bitmap *bm;
if (!(bitmap_git = prepare_bitmap_git(revs->repo)))
@@ -2540,17 +2723,28 @@ void test_bitmap_walk(struct rev_info *revs)
if (revs->pending.nr != 1)
die(_("you must specify exactly one commit to test"));
- fprintf_ln(stderr, "Bitmap v%d test (%d entries%s)",
+ fprintf_ln(stderr, "Bitmap v%d test (%d entries%s, %d total)",
bitmap_git->version,
bitmap_git->entry_count,
- bitmap_git->table_lookup ? "" : " loaded");
+ bitmap_git->table_lookup ? "" : " loaded",
+ bitmap_total_entry_count(bitmap_git));
root = revs->pending.objects[0].item;
- bm = bitmap_for_commit(bitmap_git, (struct commit *)root);
+ bm = find_bitmap_for_commit(bitmap_git, (struct commit *)root, &found);
if (bm) {
fprintf_ln(stderr, "Found bitmap for '%s'. %d bits / %08x checksum",
- oid_to_hex(&root->oid), (int)bm->bit_size, ewah_checksum(bm));
+ oid_to_hex(&root->oid),
+ (int)bm->bit_size, ewah_checksum(bm));
+
+ if (bitmap_is_midx(found))
+ fprintf_ln(stderr, "Located via MIDX '%s'.",
+ hash_to_hex_algop(get_midx_checksum(found->midx),
+ revs->repo->hash_algo));
+ else
+ fprintf_ln(stderr, "Located via pack '%s'.",
+ hash_to_hex_algop(found->pack->hash,
+ revs->repo->hash_algo));
result = ewah_to_bitmap(bm);
}
@@ -2567,16 +2761,10 @@ void test_bitmap_walk(struct rev_info *revs)
if (prepare_revision_walk(revs))
die(_("revision walk setup failed"));
- tdata.bitmap_git = bitmap_git;
- tdata.base = bitmap_new();
- tdata.commits = ewah_to_bitmap(bitmap_git->commits);
- tdata.trees = ewah_to_bitmap(bitmap_git->trees);
- tdata.blobs = ewah_to_bitmap(bitmap_git->blobs);
- tdata.tags = ewah_to_bitmap(bitmap_git->tags);
+ bitmap_test_data_prepare(&tdata, bitmap_git);
tdata.prg = start_progress(revs->repo,
"Verifying bitmap entries",
result_popcnt);
- tdata.seen = 0;
traverse_commit_list(revs, &test_show_commit, &test_show_object, &tdata);
@@ -2588,11 +2776,7 @@ void test_bitmap_walk(struct rev_info *revs)
die(_("mismatch in bitmap results"));
bitmap_free(result);
- bitmap_free(tdata.base);
- bitmap_free(tdata.commits);
- bitmap_free(tdata.trees);
- bitmap_free(tdata.blobs);
- bitmap_free(tdata.tags);
+ bitmap_test_data_release(&tdata);
free_bitmap_index(bitmap_git);
}
@@ -2820,7 +3004,7 @@ uint32_t *create_bitmap_mapping(struct bitmap_index *bitmap_git,
BUG("rebuild_existing_bitmaps: missing required rev-cache "
"extension");
- num_objects = bitmap_num_objects(bitmap_git);
+ num_objects = bitmap_num_objects_total(bitmap_git);
CALLOC_ARRAY(reposition, num_objects);
for (i = 0; i < num_objects; ++i) {
@@ -2856,6 +3040,10 @@ void free_bitmap_index(struct bitmap_index *b)
ewah_pool_free(b->trees);
ewah_pool_free(b->blobs);
ewah_pool_free(b->tags);
+ free(b->commits_all);
+ free(b->trees_all);
+ free(b->blobs_all);
+ free(b->tags_all);
if (b->bitmaps) {
struct stored_bitmap *sb;
kh_foreach_value(b->bitmaps, sb, {
@@ -2883,6 +3071,7 @@ void free_bitmap_index(struct bitmap_index *b)
close_midx_revindex(b->midx);
}
free_pseudo_merge_map(&b->pseudo_merges);
+ free_bitmap_index(b->base);
free(b);
}
@@ -2898,13 +3087,13 @@ static off_t get_disk_usage_for_type(struct bitmap_index *bitmap_git,
{
struct bitmap *result = bitmap_git->result;
off_t total = 0;
- struct ewah_iterator it;
+ struct ewah_or_iterator it;
eword_t filter;
size_t i;
init_type_iterator(&it, bitmap_git, object_type);
for (i = 0; i < result->word_alloc &&
- ewah_iterator_next(&filter, &it); i++) {
+ ewah_or_iterator_next(&filter, &it); i++) {
eword_t word = result->words[i] & filter;
size_t base = (i * BITS_IN_EWORD);
unsigned offset;
@@ -2924,7 +3113,7 @@ static off_t get_disk_usage_for_type(struct bitmap_index *bitmap_git,
off_t offset = nth_midxed_offset(bitmap_git->midx, midx_pos);
uint32_t pack_id = nth_midxed_pack_int_id(bitmap_git->midx, midx_pos);
- struct packed_git *pack = bitmap_git->midx->packs[pack_id];
+ struct packed_git *pack = nth_midxed_pack(bitmap_git->midx, pack_id);
if (offset_to_pack_pos(pack, offset, &pack_pos) < 0) {
struct object_id oid;
@@ -2945,6 +3134,8 @@ static off_t get_disk_usage_for_type(struct bitmap_index *bitmap_git,
}
}
+ ewah_or_iterator_release(&it);
+
return total;
}
@@ -2963,7 +3154,8 @@ static off_t get_disk_usage_for_extended(struct bitmap_index *bitmap_git)
struct object *obj = eindex->objects[i];
if (!bitmap_get(result,
- st_add(bitmap_num_objects(bitmap_git), i)))
+ st_add(bitmap_num_objects_total(bitmap_git),
+ i)))
continue;
if (oid_object_info_extended(bitmap_repo(bitmap_git), &obj->oid,
diff --git a/pack-bitmap.h b/pack-bitmap.h
index d7f4b8b8e9..dd0951088f 100644
--- a/pack-bitmap.h
+++ b/pack-bitmap.h
@@ -111,6 +111,7 @@ struct bitmap_writer {
kh_oid_map_t *bitmaps;
struct packing_data *to_pack;
+ struct multi_pack_index *midx; /* if appending to a MIDX chain */
struct bitmapped_commit *selected;
unsigned int selected_nr, selected_alloc;
@@ -125,7 +126,8 @@ struct bitmap_writer {
};
void bitmap_writer_init(struct bitmap_writer *writer, struct repository *r,
- struct packing_data *pdata);
+ struct packing_data *pdata,
+ struct multi_pack_index *midx);
void bitmap_writer_show_progress(struct bitmap_writer *writer, int show);
void bitmap_writer_set_checksum(struct bitmap_writer *writer,
const unsigned char *sha1);
diff --git a/pack-revindex.c b/pack-revindex.c
index d3832478d9..d3faab6a37 100644
--- a/pack-revindex.c
+++ b/pack-revindex.c
@@ -383,8 +383,14 @@ int load_midx_revindex(struct multi_pack_index *m)
trace2_data_string("load_midx_revindex", the_repository,
"source", "rev");
- get_midx_filename_ext(m->repo->hash_algo, &revindex_name, m->object_dir,
- get_midx_checksum(m), MIDX_EXT_REV);
+ if (m->has_chain)
+ get_split_midx_filename_ext(m->repo->hash_algo, &revindex_name,
+ m->object_dir, get_midx_checksum(m),
+ MIDX_EXT_REV);
+ else
+ get_midx_filename_ext(m->repo->hash_algo, &revindex_name,
+ m->object_dir, get_midx_checksum(m),
+ MIDX_EXT_REV);
ret = load_revindex_from_disk(revindex_name.buf,
m->num_objects,
@@ -471,11 +477,15 @@ off_t pack_pos_to_offset(struct packed_git *p, uint32_t pos)
uint32_t pack_pos_to_midx(struct multi_pack_index *m, uint32_t pos)
{
+ while (m && pos < m->num_objects_in_base)
+ m = m->base_midx;
+ if (!m)
+ BUG("NULL multi-pack-index for object position: %"PRIu32, pos);
if (!m->revindex_data)
BUG("pack_pos_to_midx: reverse index not yet loaded");
- if (m->num_objects <= pos)
+ if (m->num_objects + m->num_objects_in_base <= pos)
BUG("pack_pos_to_midx: out-of-bounds object at %"PRIu32, pos);
- return get_be32(m->revindex_data + pos);
+ return get_be32(m->revindex_data + pos - m->num_objects_in_base);
}
struct midx_pack_key {
@@ -491,7 +501,8 @@ static int midx_pack_order_cmp(const void *va, const void *vb)
const struct midx_pack_key *key = va;
struct multi_pack_index *midx = key->midx;
- uint32_t versus = pack_pos_to_midx(midx, (uint32_t*)vb - (const uint32_t *)midx->revindex_data);
+ size_t pos = (uint32_t *)vb - (const uint32_t *)midx->revindex_data;
+ uint32_t versus = pack_pos_to_midx(midx, pos + midx->num_objects_in_base);
uint32_t versus_pack = nth_midxed_pack_int_id(midx, versus);
off_t versus_offset;
@@ -529,9 +540,9 @@ static int midx_key_to_pack_pos(struct multi_pack_index *m,
{
uint32_t *found;
- if (key->pack >= m->num_packs)
+ if (key->pack >= m->num_packs + m->num_packs_in_base)
BUG("MIDX pack lookup out of bounds (%"PRIu32" >= %"PRIu32")",
- key->pack, m->num_packs);
+ key->pack, m->num_packs + m->num_packs_in_base);
/*
* The preferred pack sorts first, so determine its identifier by
* looking at the first object in pseudo-pack order.
@@ -551,7 +562,8 @@ static int midx_key_to_pack_pos(struct multi_pack_index *m,
if (!found)
return -1;
- *pos = found - m->revindex_data;
+ *pos = (found - m->revindex_data) + m->num_objects_in_base;
+
return 0;
}
@@ -559,9 +571,13 @@ int midx_to_pack_pos(struct multi_pack_index *m, uint32_t at, uint32_t *pos)
{
struct midx_pack_key key;
+ while (m && at < m->num_objects_in_base)
+ m = m->base_midx;
+ if (!m)
+ BUG("NULL multi-pack-index for object position: %"PRIu32, at);
if (!m->revindex_data)
BUG("midx_to_pack_pos: reverse index not yet loaded");
- if (m->num_objects <= at)
+ if (m->num_objects + m->num_objects_in_base <= at)
BUG("midx_to_pack_pos: out-of-bounds object at %"PRIu32, at);
key.pack = nth_midxed_pack_int_id(m, at);
diff --git a/pack-write.c b/pack-write.c
index d61e29ba4e..823e40b42f 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "environment.h"
#include "gettext.h"
@@ -287,7 +289,7 @@ char *write_rev_file_order(const struct git_hash_algo *hash_algo,
write_rev_index_positions(f, pack_order, nr_objects);
write_rev_trailer(hash_algo, f, hash);
- if (adjust_shared_perm(path) < 0)
+ if (adjust_shared_perm(the_repository, path) < 0)
die(_("failed to make %s readable"), path);
finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
@@ -350,7 +352,7 @@ static char *write_mtimes_file(const struct git_hash_algo *hash_algo,
write_mtimes_objects(f, to_pack, objects, nr_objects);
write_mtimes_trailer(hash_algo, f, hash);
- if (adjust_shared_perm(mtimes_name) < 0)
+ if (adjust_shared_perm(the_repository, mtimes_name) < 0)
die(_("failed to make %s readable"), mtimes_name);
finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK_METADATA,
@@ -566,12 +568,12 @@ void stage_tmp_packfiles(const struct git_hash_algo *hash_algo,
char *rev_tmp_name = NULL;
char *mtimes_tmp_name = NULL;
- if (adjust_shared_perm(pack_tmp_name))
+ if (adjust_shared_perm(the_repository, pack_tmp_name))
die_errno("unable to make temporary pack file readable");
*idx_tmp_name = (char *)write_idx_file(hash_algo, NULL, written_list,
nr_written, pack_idx_opts, hash);
- if (adjust_shared_perm(*idx_tmp_name))
+ if (adjust_shared_perm(the_repository, *idx_tmp_name))
die_errno("unable to make temporary index file readable");
rev_tmp_name = write_rev_file(hash_algo, NULL, written_list, nr_written,
diff --git a/packfile.c b/packfile.c
index 2d80d80cb3..9d09f8bc72 100644
--- a/packfile.c
+++ b/packfile.c
@@ -24,6 +24,7 @@
#include "commit-graph.h"
#include "pack-revindex.h"
#include "promisor-remote.h"
+#include "pack-mtimes.h"
char *odb_pack_name(struct repository *r, struct strbuf *buf,
const unsigned char *hash, const char *ext)
@@ -2107,7 +2108,7 @@ static void maybe_invalidate_kept_pack_cache(struct repository *r,
r->objects->kept_pack_cache.flags = 0;
}
-static struct packed_git **kept_pack_cache(struct repository *r, unsigned flags)
+struct packed_git **kept_pack_cache(struct repository *r, unsigned flags)
{
maybe_invalidate_kept_pack_cache(r, flags);
diff --git a/packfile.h b/packfile.h
index 00ada7a938..25097213d0 100644
--- a/packfile.h
+++ b/packfile.h
@@ -197,6 +197,8 @@ int has_object_pack(struct repository *r, const struct object_id *oid);
int has_object_kept_pack(struct repository *r, const struct object_id *oid,
unsigned flags);
+struct packed_git **kept_pack_cache(struct repository *r, unsigned flags);
+
/*
* Return 1 if an object in a promisor packfile is or refers to the given
* object, 0 otherwise.
diff --git a/parallel-checkout.c b/parallel-checkout.c
index 7cc6b30528..57c2dcaa8f 100644
--- a/parallel-checkout.c
+++ b/parallel-checkout.c
@@ -277,7 +277,7 @@ static int write_pc_item_to_fd(struct parallel_checkout_item *pc_item, int fd,
ssize_t wrote;
/* Sanity check */
- assert(is_eligible_for_parallel_checkout(pc_item->ce, &pc_item->ca));
+ ASSERT(is_eligible_for_parallel_checkout(pc_item->ce, &pc_item->ca));
filter = get_stream_filter_ca(&pc_item->ca, &pc_item->ce->oid);
if (filter) {
diff --git a/parse-options.h b/parse-options.h
index fca944d9a9..997ffbee80 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -6,7 +6,7 @@
struct repository;
/**
- * Refer to Documentation/technical/api-parse-options.txt for the API doc.
+ * Refer to Documentation/technical/api-parse-options.adoc for the API doc.
*/
enum parse_opt_type {
diff --git a/path.c b/path.c
index 07964f5d32..910756c8b3 100644
--- a/path.c
+++ b/path.c
@@ -2,8 +2,6 @@
* Utilities for paths and pathnames
*/
-#define USE_THE_REPOSITORY_VARIABLE
-
#include "git-compat-util.h"
#include "abspath.h"
#include "environment.h"
@@ -30,7 +28,7 @@ static int get_st_mode_bits(const char *path, int *mode)
return 0;
}
-struct strbuf *get_pathname(void)
+static struct strbuf *get_pathname(void)
{
static struct strbuf pathname_array[4] = {
STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
@@ -387,10 +385,11 @@ void report_linked_checkout_garbage(struct repository *r)
strbuf_release(&sb);
}
-static void adjust_git_path(const struct repository *repo,
+static void adjust_git_path(struct repository *repo,
struct strbuf *buf, int git_dir_len)
{
const char *base = buf->buf + git_dir_len;
+
if (is_dir_file(base, "info", "grafts"))
strbuf_splice(buf, 0, buf->len,
repo->graft_file, strlen(repo->graft_file));
@@ -399,8 +398,8 @@ static void adjust_git_path(const struct repository *repo,
repo->index_file, strlen(repo->index_file));
else if (dir_prefix(base, "objects"))
replace_dir(buf, git_dir_len + 7, repo->objects->odb->path);
- else if (git_hooks_path && dir_prefix(base, "hooks"))
- replace_dir(buf, git_dir_len + 5, git_hooks_path);
+ else if (repo_settings_get_hooks_path(repo) && dir_prefix(base, "hooks"))
+ replace_dir(buf, git_dir_len + 5, repo_settings_get_hooks_path(repo));
else if (repo->different_commondir)
update_common_dir(buf, git_dir_len, repo->commondir);
}
@@ -414,12 +413,12 @@ static void strbuf_worktree_gitdir(struct strbuf *buf,
else if (!wt->id)
strbuf_addstr(buf, repo->commondir);
else
- strbuf_git_common_path(buf, repo, "worktrees/%s", wt->id);
+ repo_common_path_append(repo, buf, "worktrees/%s", wt->id);
}
-void repo_git_pathv(const struct repository *repo,
- const struct worktree *wt, struct strbuf *buf,
- const char *fmt, va_list args)
+static void repo_git_pathv(struct repository *repo,
+ const struct worktree *wt, struct strbuf *buf,
+ const char *fmt, va_list args)
{
int gitdir_len;
strbuf_worktree_gitdir(buf, repo, wt);
@@ -432,7 +431,7 @@ void repo_git_pathv(const struct repository *repo,
strbuf_cleanup_path(buf);
}
-char *repo_git_path(const struct repository *repo,
+char *repo_git_path(struct repository *repo,
const char *fmt, ...)
{
struct strbuf path = STRBUF_INIT;
@@ -443,14 +442,27 @@ char *repo_git_path(const struct repository *repo,
return strbuf_detach(&path, NULL);
}
-void strbuf_repo_git_path(struct strbuf *sb,
- const struct repository *repo,
- const char *fmt, ...)
+const char *repo_git_path_append(struct repository *repo,
+ struct strbuf *sb,
+ const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
repo_git_pathv(repo, NULL, sb, fmt, args);
va_end(args);
+ return sb->buf;
+}
+
+const char *repo_git_path_replace(struct repository *repo,
+ struct strbuf *sb,
+ const char *fmt, ...)
+{
+ va_list args;
+ strbuf_reset(sb);
+ va_start(args, fmt);
+ repo_git_pathv(repo, NULL, sb, fmt, args);
+ va_end(args);
+ return sb->buf;
}
char *mkpathdup(const char *fmt, ...)
@@ -506,39 +518,56 @@ char *repo_worktree_path(const struct repository *repo, const char *fmt, ...)
struct strbuf path = STRBUF_INIT;
va_list args;
+ va_start(args, fmt);
+ do_worktree_path(repo, &path, fmt, args);
+ va_end(args);
+
+ return strbuf_detach(&path, NULL);
+}
+
+const char *repo_worktree_path_append(const struct repository *repo,
+ struct strbuf *sb,
+ const char *fmt, ...)
+{
+ va_list args;
+
if (!repo->worktree)
return NULL;
va_start(args, fmt);
- do_worktree_path(repo, &path, fmt, args);
+ do_worktree_path(repo, sb, fmt, args);
va_end(args);
- return strbuf_detach(&path, NULL);
+ return sb->buf;
}
-void strbuf_repo_worktree_path(struct strbuf *sb,
- const struct repository *repo,
- const char *fmt, ...)
+const char *repo_worktree_path_replace(const struct repository *repo,
+ struct strbuf *sb,
+ const char *fmt, ...)
{
va_list args;
+ strbuf_reset(sb);
if (!repo->worktree)
- return;
+ return NULL;
va_start(args, fmt);
do_worktree_path(repo, sb, fmt, args);
va_end(args);
+
+ return sb->buf;
}
/* Returns 0 on success, negative on failure. */
-static int do_submodule_path(struct strbuf *buf, const char *path,
+static int do_submodule_path(struct repository *repo,
+ struct strbuf *buf, const char *path,
const char *fmt, va_list args)
{
struct strbuf git_submodule_common_dir = STRBUF_INIT;
struct strbuf git_submodule_dir = STRBUF_INIT;
int ret;
- ret = submodule_to_gitdir(&git_submodule_dir, path);
+ ret = submodule_to_gitdir(repo, &git_submodule_dir, path);
if (ret)
goto cleanup;
@@ -557,13 +586,14 @@ cleanup:
return ret;
}
-char *git_pathdup_submodule(const char *path, const char *fmt, ...)
+char *repo_submodule_path(struct repository *repo,
+ const char *path, const char *fmt, ...)
{
int err;
va_list args;
struct strbuf buf = STRBUF_INIT;
va_start(args, fmt);
- err = do_submodule_path(&buf, path, fmt, args);
+ err = do_submodule_path(repo, &buf, path, fmt, args);
va_end(args);
if (err) {
strbuf_release(&buf);
@@ -572,22 +602,41 @@ char *git_pathdup_submodule(const char *path, const char *fmt, ...)
return strbuf_detach(&buf, NULL);
}
-int strbuf_git_path_submodule(struct strbuf *buf, const char *path,
- const char *fmt, ...)
+const char *repo_submodule_path_append(struct repository *repo,
+ struct strbuf *buf,
+ const char *path,
+ const char *fmt, ...)
{
int err;
va_list args;
va_start(args, fmt);
- err = do_submodule_path(buf, path, fmt, args);
+ err = do_submodule_path(repo, buf, path, fmt, args);
va_end(args);
+ if (err)
+ return NULL;
+ return buf->buf;
+}
- return err;
+const char *repo_submodule_path_replace(struct repository *repo,
+ struct strbuf *buf,
+ const char *path,
+ const char *fmt, ...)
+{
+ int err;
+ va_list args;
+ strbuf_reset(buf);
+ va_start(args, fmt);
+ err = do_submodule_path(repo, buf, path, fmt, args);
+ va_end(args);
+ if (err)
+ return NULL;
+ return buf->buf;
}
-void repo_common_pathv(const struct repository *repo,
- struct strbuf *sb,
- const char *fmt,
- va_list args)
+static void repo_common_pathv(const struct repository *repo,
+ struct strbuf *sb,
+ const char *fmt,
+ va_list args)
{
strbuf_addstr(sb, repo->commondir);
if (sb->len && !is_dir_sep(sb->buf[sb->len - 1]))
@@ -596,14 +645,38 @@ void repo_common_pathv(const struct repository *repo,
strbuf_cleanup_path(sb);
}
-void strbuf_git_common_path(struct strbuf *sb,
- const struct repository *repo,
- const char *fmt, ...)
+char *repo_common_path(const struct repository *repo,
+ const char *fmt, ...)
{
+ struct strbuf sb = STRBUF_INIT;
va_list args;
va_start(args, fmt);
+ repo_common_pathv(repo, &sb, fmt, args);
+ va_end(args);
+ return strbuf_detach(&sb, NULL);
+}
+
+const char *repo_common_path_append(const struct repository *repo,
+ struct strbuf *sb,
+ const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ repo_common_pathv(repo, sb, fmt, args);
+ va_end(args);
+ return sb->buf;
+}
+
+const char *repo_common_path_replace(const struct repository *repo,
+ struct strbuf *sb,
+ const char *fmt, ...)
+{
+ va_list args;
+ strbuf_reset(sb);
+ va_start(args, fmt);
repo_common_pathv(repo, sb, fmt, args);
va_end(args);
+ return sb->buf;
}
static struct passwd *getpw_str(const char *username, size_t len)
@@ -765,21 +838,22 @@ const char *enter_repo(const char *path, unsigned flags)
return NULL;
}
-int calc_shared_perm(int mode)
+int calc_shared_perm(struct repository *repo,
+ int mode)
{
int tweak;
- if (get_shared_repository() < 0)
- tweak = -get_shared_repository();
+ if (repo_settings_get_shared_repository(repo) < 0)
+ tweak = -repo_settings_get_shared_repository(repo);
else
- tweak = get_shared_repository();
+ tweak = repo_settings_get_shared_repository(repo);
if (!(mode & S_IWUSR))
tweak &= ~0222;
if (mode & S_IXUSR)
/* Copy read bits to execute bits */
tweak |= (tweak & 0444) >> 2;
- if (get_shared_repository() < 0)
+ if (repo_settings_get_shared_repository(repo) < 0)
mode = (mode & ~0777) | tweak;
else
mode |= tweak;
@@ -787,17 +861,17 @@ int calc_shared_perm(int mode)
return mode;
}
-
-int adjust_shared_perm(const char *path)
+int adjust_shared_perm(struct repository *repo,
+ const char *path)
{
int old_mode, new_mode;
- if (!get_shared_repository())
+ if (!repo_settings_get_shared_repository(repo))
return 0;
if (get_st_mode_bits(path, &old_mode) < 0)
return -1;
- new_mode = calc_shared_perm(old_mode);
+ new_mode = calc_shared_perm(repo, old_mode);
if (S_ISDIR(old_mode)) {
/* Copy read bits to execute bits */
new_mode |= (new_mode & 0444) >> 2;
@@ -816,7 +890,7 @@ int adjust_shared_perm(const char *path)
return 0;
}
-void safe_create_dir(const char *dir, int share)
+void safe_create_dir(struct repository *repo, const char *dir, int share)
{
if (mkdir(dir, 0777) < 0) {
if (errno != EEXIST) {
@@ -824,7 +898,7 @@ void safe_create_dir(const char *dir, int share)
exit(1);
}
}
- else if (share && adjust_shared_perm(dir))
+ else if (share && adjust_shared_perm(repo, dir))
die(_("Could not make %s writable by group"), dir);
}
diff --git a/path.h b/path.h
index 5f6c85e5f8..65fe968a13 100644
--- a/path.h
+++ b/path.h
@@ -25,22 +25,20 @@ char *mkpathdup(const char *fmt, ...)
__attribute__((format (printf, 1, 2)));
/*
- * The `strbuf_git_common_path` family of functions will construct a path into a
+ * The `repo_common_path` family of functions will construct a path into a
* repository's common git directory, which is shared by all worktrees.
*/
-
-/*
- * Constructs a path into the common git directory of repository `repo` and
- * append it in the provided buffer `sb`.
- */
-void strbuf_git_common_path(struct strbuf *sb,
- const struct repository *repo,
- const char *fmt, ...)
+char *repo_common_path(const struct repository *repo,
+ const char *fmt, ...)
+ __attribute__((format (printf, 2, 3)));
+const char *repo_common_path_append(const struct repository *repo,
+ struct strbuf *sb,
+ const char *fmt, ...)
+ __attribute__((format (printf, 3, 4)));
+const char *repo_common_path_replace(const struct repository *repo,
+ struct strbuf *sb,
+ const char *fmt, ...)
__attribute__((format (printf, 3, 4)));
-void repo_common_pathv(const struct repository *repo,
- struct strbuf *buf,
- const char *fmt,
- va_list args);
/*
* The `repo_git_path` family of functions will construct a path into a repository's
@@ -54,29 +52,16 @@ void repo_common_pathv(const struct repository *repo,
* For an exhaustive list of the adjustments made look at `common_list` and
* `adjust_git_path` in path.c.
*/
-
-/*
- * Return a path into the git directory of repository `repo`.
- */
-char *repo_git_path(const struct repository *repo,
+char *repo_git_path(struct repository *repo,
const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
-
-/*
- * Print a path into the git directory of repository `repo` into the provided
- * buffer.
- */
-void repo_git_pathv(const struct repository *repo,
- const struct worktree *wt, struct strbuf *buf,
- const char *fmt, va_list args);
-
-/*
- * Construct a path into the git directory of repository `repo` and append it
- * to the provided buffer `sb`.
- */
-void strbuf_repo_git_path(struct strbuf *sb,
- const struct repository *repo,
- const char *fmt, ...)
+const char *repo_git_path_append(struct repository *repo,
+ struct strbuf *sb,
+ const char *fmt, ...)
+ __attribute__((format (printf, 3, 4)));
+const char *repo_git_path_replace(struct repository *repo,
+ struct strbuf *sb,
+ const char *fmt, ...)
__attribute__((format (printf, 3, 4)));
/*
@@ -90,40 +75,44 @@ const char *worktree_git_path(struct repository *r,
__attribute__((format (printf, 3, 4)));
/*
- * Return a path into the worktree of repository `repo`.
+ * The `repo_worktree_path` family of functions will construct a path into a
+ * repository's worktree.
*
- * If the repository doesn't have a worktree NULL is returned.
+ * Returns a `NULL` pointer in case the repository has no worktree.
*/
char *repo_worktree_path(const struct repository *repo,
const char *fmt, ...)
__attribute__((format (printf, 2, 3)));
-
-/*
- * Construct a path into the worktree of repository `repo` and append it
- * to the provided buffer `sb`.
- *
- * If the repository doesn't have a worktree nothing will be appended to `sb`.
- */
-void strbuf_repo_worktree_path(struct strbuf *sb,
- const struct repository *repo,
+const char *repo_worktree_path_append(const struct repository *repo,
+ struct strbuf *sb,
const char *fmt, ...)
__attribute__((format (printf, 3, 4)));
+const char *repo_worktree_path_replace(const struct repository *repo,
+ struct strbuf *sb,
+ const char *fmt, ...)
+ __attribute__((format (printf, 3, 4)));
/*
- * Return a path into a submodule's git directory located at `path`. `path`
- * must only reference a submodule of the main repository (the_repository).
- */
-char *git_pathdup_submodule(const char *path, const char *fmt, ...)
- __attribute__((format (printf, 2, 3)));
-
-/*
- * Construct a path into a submodule's git directory located at `path` and
- * append it to the provided buffer `sb`. `path` must only reference a
- * submodule of the main repository (the_repository).
+ * The `repo_submodule_path` family of functions will construct a path into a
+ * submodule's git directory located at `path`. `path` must be a submodule path
+ * as found in the index and must be part of the given repository.
+ *
+ * Returns a `NULL` pointer in case the submodule cannot be found.
*/
-int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
- const char *fmt, ...)
+char *repo_submodule_path(struct repository *repo,
+ const char *path,
+ const char *fmt, ...)
__attribute__((format (printf, 3, 4)));
+const char *repo_submodule_path_append(struct repository *repo,
+ struct strbuf *sb,
+ const char *path,
+ const char *fmt, ...)
+ __attribute__((format (printf, 4, 5)));
+const char *repo_submodule_path_replace(struct repository *repo,
+ struct strbuf *sb,
+ const char *path,
+ const char *fmt, ...)
+ __attribute__((format (printf, 4, 5)));
void report_linked_checkout_garbage(struct repository *r);
@@ -152,8 +141,8 @@ const char *git_path_shallow(struct repository *r);
int ends_with_path_components(const char *path, const char *components);
-int calc_shared_perm(int mode);
-int adjust_shared_perm(const char *path);
+int calc_shared_perm(struct repository *repo, int mode);
+int adjust_shared_perm(struct repository *repo, const char *path);
char *interpolate_path(const char *path, int real_home);
@@ -230,101 +219,21 @@ char *xdg_cache_home(const char *filename);
* directories under $GIT_DIR. Don't use it for working tree
* directories.
*/
-void safe_create_dir(const char *dir, int share);
-
-/*
- * Do not use this function. It is only exported to other subsystems until we
- * can get rid of the below block of functions that implicitly rely on
- * `the_repository`.
- */
-struct strbuf *get_pathname(void);
+void safe_create_dir(struct repository *repo, const char *dir, int share);
# ifdef USE_THE_REPOSITORY_VARIABLE
# include "strbuf.h"
# include "repository.h"
-/*
- * Return a statically allocated path into the main repository's
- * (the_repository) common git directory.
- */
-__attribute__((format (printf, 1, 2)))
-static inline const char *git_common_path(const char *fmt, ...)
-{
- struct strbuf *pathname = get_pathname();
- va_list args;
- va_start(args, fmt);
- repo_common_pathv(the_repository, pathname, fmt, args);
- va_end(args);
- return pathname->buf;
-}
-
-/*
- * Construct a path into the main repository's (the_repository) git directory
- * and place it in the provided buffer `buf`, the contents of the buffer will
- * be overridden.
- */
-__attribute__((format (printf, 2, 3)))
-static inline char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
-{
- va_list args;
- strbuf_reset(buf);
- va_start(args, fmt);
- repo_git_pathv(the_repository, NULL, buf, fmt, args);
- va_end(args);
- return buf->buf;
-}
-
-/*
- * Construct a path into the main repository's (the_repository) git directory
- * and append it to the provided buffer `sb`.
- */
-__attribute__((format (printf, 2, 3)))
-static inline void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
- repo_git_pathv(the_repository, NULL, sb, fmt, args);
- va_end(args);
-}
-
-/*
- * Return a statically allocated path into the main repository's
- * (the_repository) git directory.
- */
-__attribute__((format (printf, 1, 2)))
-static inline const char *git_path(const char *fmt, ...)
-{
- struct strbuf *pathname = get_pathname();
- va_list args;
- va_start(args, fmt);
- repo_git_pathv(the_repository, NULL, pathname, fmt, args);
- va_end(args);
- return pathname->buf;
-}
-
#define GIT_PATH_FUNC(func, filename) \
const char *func(void) \
{ \
static char *ret; \
if (!ret) \
- ret = git_pathdup(filename); \
+ ret = repo_git_path(the_repository, filename); \
return ret; \
}
-/*
- * Return a path into the main repository's (the_repository) git directory.
- */
-__attribute__((format (printf, 1, 2)))
-static inline char *git_pathdup(const char *fmt, ...)
-{
- struct strbuf path = STRBUF_INIT;
- va_list args;
- va_start(args, fmt);
- repo_git_pathv(the_repository, NULL, &path, fmt, args);
- va_end(args);
- return strbuf_detach(&path, NULL);
-}
-
# endif /* USE_THE_REPOSITORY_VARIABLE */
#endif /* PATH_H */
diff --git a/po/bg.po b/po/bg.po
index 2fc6c2b60c..37d127faac 100644
--- a/po/bg.po
+++ b/po/bg.po
@@ -1,7 +1,7 @@
# Bulgarian translation of git po-file.
-# Copyright (C) 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024 Alexander Shopov <ash@kambanaria.org>.
+# Copyright (C) 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025 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, 2024.
+# Alexander Shopov <ash@kambanaria.org>, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025.
# ========================
# DICTIONARY TO MERGE IN GIT GUI
# ------------------------
@@ -233,6 +233,7 @@
# cache-tree кеша на обектите-дървета
# acquire lock придобивам ключалка
# detached отделѐн, несвързан
+# revision walk обхождане на версиите
#
# ------------------------
# „$var“ - може да не сработва за shell има gettext и eval_gettext - проверка - намират се лесно по „$
@@ -261,8 +262,8 @@ msgid ""
msgstr ""
"Project-Id-Version: git 2.48\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2024-12-27 22:37+0100\n"
-"PO-Revision-Date: 2024-12-27 22:40+0100\n"
+"POT-Creation-Date: 2025-03-05 22:57+0000\n"
+"PO-Revision-Date: 2025-03-06 09:15+0100\n"
"Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
"Language-Team: Bulgarian <dict@fsa-bg.org>\n"
"Language: bg\n"
@@ -2655,6 +2656,18 @@ msgstr "git archive: протоколна грешка"
msgid "git archive: expected a flush"
msgstr "git archive: очакваше се изчистване на буферите чрез „flush“"
+msgid "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+msgstr "git backfill [--min-batch-size=БРОЙ] [--[no-]sparse]"
+
+msgid "problem loading sparse-checkout"
+msgstr "проблем при зареждане на частично хранилище"
+
+msgid "Minimum number of objects to request at a time"
+msgstr "Минимален БРОЙ обекти заявявани наведнъж"
+
+msgid "Restrict the missing objects to the current sparse-checkout"
+msgstr "Ограничаване на липсващите обекти до текущото частично хранилище"
+
msgid ""
"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>] [--no-"
"checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]"
@@ -3434,10 +3447,6 @@ msgstr ""
msgid "git version:\n"
msgstr "версия на git:\n"
-#, c-format
-msgid "uname() failed with error '%s' (%d)\n"
-msgstr "грешка при изпълнението на „uname()“ — „%s“ (%d)\n"
-
msgid "compiler info: "
msgstr "компилатор: "
@@ -4466,8 +4475,98 @@ msgstr ""
"Настройката „clean.requireForce“ е зададена, което изисква опцията „-f“. "
"Няма да се извърши изчистване"
-msgid "git clone [<options>] [--] <repo> [<dir>]"
-msgstr "git clone [ОПЦИЯ…] [--] ХРАНИЛИЩЕ [ДИРЕКТОРИЯ]"
+#, c-format
+msgid "info: Could not add alternate for '%s': %s\n"
+msgstr ""
+"ПРЕДУПРЕЖДЕНИЕ: не може да се добави алтернативен източник на „%s“: %s\n"
+
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "не може да бъде получена информация чрез „stat“ за „%s“"
+
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "„%s“ съществува и не е директория"
+
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "„%s“ е символна връзка, не може да се клонира с опцията „--local“"
+
+#, c-format
+msgid "failed to start iterator over '%s'"
+msgstr "неуспешно итериране по „%s“"
+
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr ""
+"символната връзка „%s“ съществува, не може да се клонира с опцията „--local“"
+
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "неуспешно изтриване на „%s“"
+
+#, c-format
+msgid "hardlink cannot be checked at '%s'"
+msgstr "твърдата връзка не може да се провери при „%s“"
+
+#, c-format
+msgid "hardlink different from source at '%s'"
+msgstr "твърдата връзка е различна от източника „%s“"
+
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "връзката „%s“ не може да бъде създадена"
+
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "файлът не може да бъде копиран като „%s“"
+
+#, c-format
+msgid "failed to iterate over '%s'"
+msgstr "неуспешно итериране по „%s“"
+
+#, c-format
+msgid "done.\n"
+msgstr "действието завърши.\n"
+
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry with 'git restore --source=HEAD :/'\n"
+msgstr ""
+"Клонирането бе успешно за разлика от подготовката на работното дърво\n"
+"за определен клон. Все пак може да проверите кои файлове и от кой\n"
+"клон в момента са изтеглени с командата „git status“. Може да\n"
+"завършите изтеглянето на клона с командата:\n"
+"\n"
+" git restore --source=HEAD :/\n"
+
+msgid "remote did not send all necessary objects"
+msgstr "отдалеченото хранилище не изпрати всички необходими обекти."
+
+#, c-format
+msgid "unable to update %s"
+msgstr "обектът „%s“ не може да бъде обновен"
+
+msgid "failed to initialize sparse-checkout"
+msgstr "частичното изтегляне не може да се инициализира"
+
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
+msgstr ""
+"указателят „HEAD“ от отдалеченото хранилище сочи към нещо, което не "
+"съществува. Изтегляне не може да се извърши"
+
+msgid "unable to checkout working tree"
+msgstr "работното дърво не може да бъде подготвено"
+
+msgid "unable to write parameters to config file"
+msgstr "настройките не може да бъдат записани в конфигурационния файл"
+
+msgid "cannot repack to clean up"
+msgstr "не може да се извърши пакетиране за изчистване на файловете"
+
+msgid "cannot unlink temporary alternates file"
+msgstr "временният файл за алтернативни обекти не може да бъде изтрит"
msgid "don't clone shallow repository"
msgstr "без клониране на плитко хранилище"
@@ -4521,6 +4620,9 @@ msgstr "използване на това ИМЕ вместо „origin“ пр
msgid "checkout <branch> instead of the remote's HEAD"
msgstr "изтегляне на този КЛОН, а не соченият от отдалечения указател „HEAD“"
+msgid "clone single revision <rev> and check out"
+msgstr "клониране на единствена ВЕРСИЯ и изтегляне в работната директория"
+
msgid "path to git-upload-pack on the remote"
msgstr "път към командата „git-upload-pack“ на отдалеченото хранилище"
@@ -4544,9 +4646,8 @@ msgstr ""
"клониране само на един клон — или сочения от отдалечения „HEAD“, или изрично "
"зададения с „--branch“"
-msgid "don't clone any tags, and make later fetches not to follow them"
-msgstr ""
-"без клониране на етикети, като последващите доставяния няма да ги следят"
+msgid "clone tags, and make later fetches not to follow them"
+msgstr "клониране на етикети, като последващите доставяния няма да ги следят"
msgid "any cloned submodules will be shallow"
msgstr "всички клонирани подмодули ще са плитки"
@@ -4590,104 +4691,8 @@ msgid "a URI for downloading bundles before fetching from origin remote"
msgstr ""
"АДРЕС за доставяне на пратки на git преди доставяне от отдалеченото хранилище"
-#, c-format
-msgid "info: Could not add alternate for '%s': %s\n"
-msgstr ""
-"ПРЕДУПРЕЖДЕНИЕ: не може да се добави алтернативен източник на „%s“: %s\n"
-
-#, c-format
-msgid "failed to stat '%s'"
-msgstr "не може да бъде получена информация чрез „stat“ за „%s“"
-
-#, c-format
-msgid "%s exists and is not a directory"
-msgstr "„%s“ съществува и не е директория"
-
-#, c-format
-msgid "'%s' is a symlink, refusing to clone with --local"
-msgstr "„%s“ е символна връзка, не може да се клонира с опцията „--local“"
-
-#, c-format
-msgid "failed to start iterator over '%s'"
-msgstr "неуспешно итериране по „%s“"
-
-#, c-format
-msgid "symlink '%s' exists, refusing to clone with --local"
-msgstr ""
-"символната връзка „%s“ съществува, не може да се клонира с опцията „--local“"
-
-#, c-format
-msgid "failed to unlink '%s'"
-msgstr "неуспешно изтриване на „%s“"
-
-#, c-format
-msgid "hardlink cannot be checked at '%s'"
-msgstr "твърдата връзка не може да се провери при „%s“"
-
-#, c-format
-msgid "hardlink different from source at '%s'"
-msgstr "твърдата връзка е различна от източника „%s“"
-
-#, c-format
-msgid "failed to create link '%s'"
-msgstr "връзката „%s“ не може да бъде създадена"
-
-#, c-format
-msgid "failed to copy file to '%s'"
-msgstr "файлът не може да бъде копиран като „%s“"
-
-#, c-format
-msgid "failed to iterate over '%s'"
-msgstr "неуспешно итериране по „%s“"
-
-#, c-format
-msgid "done.\n"
-msgstr "действието завърши.\n"
-
-msgid ""
-"Clone succeeded, but checkout failed.\n"
-"You can inspect what was checked out with 'git status'\n"
-"and retry with 'git restore --source=HEAD :/'\n"
-msgstr ""
-"Клонирането бе успешно за разлика от подготовката на работното дърво\n"
-"за определен клон. Все пак може да проверите кои файлове и от кой\n"
-"клон в момента са изтеглени с командата „git status“. Може да\n"
-"завършите изтеглянето на клона с командата:\n"
-"\n"
-" git restore --source=HEAD :/\n"
-
-#, c-format
-msgid "Could not find remote branch %s to clone."
-msgstr ""
-"Клонът „%s“ от отдалеченото хранилище, което клонирате,\n"
-"и който следва да бъде изтеглен, не съществува."
-
-msgid "remote did not send all necessary objects"
-msgstr "отдалеченото хранилище не изпрати всички необходими обекти."
-
-#, c-format
-msgid "unable to update %s"
-msgstr "обектът „%s“ не може да бъде обновен"
-
-msgid "failed to initialize sparse-checkout"
-msgstr "частичното изтегляне не може да се инициализира"
-
-msgid "remote HEAD refers to nonexistent ref, unable to checkout"
-msgstr ""
-"указателят „HEAD“ от отдалеченото хранилище сочи към нещо, което не "
-"съществува. Изтегляне не може да се извърши"
-
-msgid "unable to checkout working tree"
-msgstr "работното дърво не може да бъде подготвено"
-
-msgid "unable to write parameters to config file"
-msgstr "настройките не може да бъдат записани в конфигурационния файл"
-
-msgid "cannot repack to clean up"
-msgstr "не може да се извърши пакетиране за изчистване на файловете"
-
-msgid "cannot unlink temporary alternates file"
-msgstr "временният файл за алтернативни обекти не може да бъде изтрит"
+msgid "git clone [<options>] [--] <repo> [<dir>]"
+msgstr "git clone [ОПЦИЯ…] [--] ХРАНИЛИЩЕ [ДИРЕКТОРИЯ]"
msgid "Too many arguments."
msgstr "Прекалено много аргументи."
@@ -4796,6 +4801,10 @@ msgstr "отдалеченият транспорт върна грешка"
msgid "Remote branch %s not found in upstream %s"
msgstr "Отдалеченият клон „%s“ липсва в клонираното хранилище „%s“"
+#, c-format
+msgid "Remote revision %s not found in upstream %s"
+msgstr "Отдалечената версия „%s“ липсва в клонираното хранилище „%s“"
+
msgid "You appear to have cloned an empty repository."
msgstr "Изглежда клонирахте празно хранилище."
@@ -4975,7 +4984,7 @@ msgid "git commit-tree: failed to read"
msgstr "git commit-tree: не може да се прочете"
msgid ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
"reword):]<commit>]\n"
" [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
@@ -4985,7 +4994,7 @@ msgid ""
" [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
" [--] [<pathspec>...]"
msgstr ""
-"git commit [-a|--interactive|--patch] [-s] [-v] [-u РЕЖИМ] [--amend]\n"
+"git commit [-a|--interactive|--patch] [-s] [-v] [-u[РЕЖИМ]] [--amend]\n"
" [--dry-run] [(-c|-C|--squash) ПОДАВАНЕ |--fixup [(amend|"
"reword):]ПОДАВАНЕ]\n"
" [-F ФАЙЛ|-m СЪОБЩЕНИЕ] [--reset-author] [--allow-empty]\n"
@@ -6424,8 +6433,8 @@ msgid ""
"Run 'git remote set-head %s %s' to follow the change, or set\n"
"'remote.%s.followRemoteHEAD' configuration option to a different value\n"
"if you do not want to see this message. Specifically running\n"
-"'git config set remote.%s.followRemoteHEAD %s' will disable the warning\n"
-"until the remote changes HEAD to something else."
+"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n"
+"will disable the warning until the remote changes HEAD to something else."
msgstr ""
"Изпълнете\n"
"\n"
@@ -6435,7 +6444,7 @@ msgstr ""
"„remote.%s.followRemoteHEAD, ако не искате тези съобщения.\n"
"Изпълнението на\n"
"\n"
-" git config set remote.%s.followRemoteHEAD %s\n"
+" git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s\n"
"\n"
"ще изключи предупреждението, докато отдалеченият указател HEAD не\n"
"започне да сочи нещо друго."
@@ -7124,6 +7133,9 @@ msgstr ""
msgid "repack all other packs except the largest pack"
msgstr "препакетиране на всичко без най-големия пакет"
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr "префикс на имената на пакетите за окастрени обекти"
+
#, c-format
msgid "failed to parse gc.logExpiry value %s"
msgstr "неразпозната стойност на „gc.logExpiry“ %s"
@@ -7944,6 +7956,10 @@ msgid "Cannot come back to cwd"
msgstr "Процесът не може да се върне към предишната работна директория"
#, c-format
+msgid "bad --pack_header: %s"
+msgstr "неправилна стойност за „--pack_header“: „%s“"
+
+#, c-format
msgid "bad %s"
msgstr "неправилна стойност „%s“"
@@ -8835,10 +8851,6 @@ msgstr "непозната опция за стратегия: -X%s"
msgid "malformed input line: '%s'."
msgstr "входен ред с неправилен формат: „%s“."
-#, c-format
-msgid "merging cannot continue; got unclean result of %d"
-msgstr "сливането не може да продължи — %d-тото завърши с грешка"
-
msgid "git merge [<options>] [<commit>...]"
msgstr "git merge [ОПЦИЯ…] [ПОДАВАНЕ…]"
@@ -9685,6 +9697,13 @@ msgstr ""
"СПИСЪК_С_ОБЕКТИ]"
#, c-format
+msgid "invalid --name-hash-version option: %d"
+msgstr "неправилна опция „--name-hash-version“: %d"
+
+msgid "currently, --write-bitmap-index requires --name-hash-version=1"
+msgstr "текущо опцията „--write-bitmap-index“ изисква „--name-hash-version=1“"
+
+#, c-format
msgid ""
"write_reuse_object: could not locate %s, expected at offset %<PRIuMAX> in "
"pack %s"
@@ -10025,6 +10044,11 @@ msgstr "протокол"
msgid "exclude any configured uploadpack.blobpackfileuri with this protocol"
msgstr "без ползване на настройки „uploadpack.blobpackfileuri“ с този протокол"
+msgid "use the specified name-hash function to group similar objects"
+msgstr ""
+"използване на указаната функция за контролни суми за групиране на подобните "
+"обекти"
+
#, c-format
msgid "delta chain depth %d is too deep, forcing %d"
msgstr "веригата с разлики е прекалено дълбока — %d, ще се ползва %d"
@@ -11304,8 +11328,8 @@ msgstr "не е указан журнал с подавания за изтри�
msgid "invalid ref format: %s"
msgstr "неправилен формат на указател: %s"
-msgid "git refs migrate --ref-format=<format> [--dry-run]"
-msgstr "git refs migrate --ref-format=ФОРМАТ [--dry-run]"
+msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
+msgstr "git refs migrate --ref-format=ФОРМАТ [--no-reflog] [--dry-run]"
msgid "git refs verify [--strict] [--verbose]"
msgstr "git refs verify [--strict] [--verbose]"
@@ -11316,6 +11340,9 @@ msgstr "указване на форма̀та за указател, към к�
msgid "perform a non-destructive dry-run"
msgstr "пробно изпълнение — без промяна на данни"
+msgid "drop reflogs entirely during the migration"
+msgstr "журналът на указателите да се изтрие изцяло по време на миграцията"
+
msgid "missing --ref-format=<format>"
msgstr "липсва опцията --ref-format=ФОРМАТ"
@@ -11786,8 +11813,14 @@ msgstr "Никой от адресите, които не са за изтлас
msgid "be verbose; must be placed before a subcommand"
msgstr "повече подробности. Поставя се пред подкоманда"
-msgid "git repack [<options>]"
-msgstr "git repack [ОПЦИЯ…]"
+msgid ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
+msgstr ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=БРОЙ] [--depth=БРОЙ] [--threads=БРОЙ] [--keep-pack=ИМЕ_НА_ПАКЕТ]\n"
+"[--write-midx] [--name-hash-version=БРОЙ]"
msgid ""
"Incremental repacks are incompatible with bitmap indexes. Use\n"
@@ -11875,6 +11908,11 @@ msgid "pass --no-reuse-object to git-pack-objects"
msgstr ""
"подаване на опцията „--no-reuse-object“ на командата „git-pack-objects“"
+msgid ""
+"specify the name hash version to use for grouping similar objects by path"
+msgstr ""
+"укажете функция за контролни суми за групиране на подобните обекти по път"
+
msgid "do not run git-update-server-info"
msgstr "без изпълнение на командата „git-update-server-info“"
@@ -11927,9 +11965,6 @@ msgstr "откриване на геометрична прогресия с ч�
msgid "write a multi-pack index of the resulting packs"
msgstr "запазване на многопакетен индекс за създадените пакети"
-msgid "pack prefix to store a pack containing pruned objects"
-msgstr "префикс на имената на пакетите за окастрени обекти"
-
msgid "pack prefix to store a pack containing filtered out objects"
msgstr "префикс на имената на пакетите за филтрирани обекти"
@@ -12146,9 +12181,6 @@ 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 "всички зададени положителни версии трябва да са указатели"
@@ -14852,6 +14884,9 @@ msgstr "Внасяне на хранилище на GNU Arch в Git"
msgid "Create an archive of files from a named tree"
msgstr "Създаване на архив с файловете от именовано дърво"
+msgid "Download missing objects in a partial clone"
+msgstr "Изтегляне на липсващите обекти в частично хранилище"
+
msgid "Use binary search to find the commit that introduced a bug"
msgstr "Двоично търсене на промяната, която е причинила грешка"
@@ -16852,6 +16887,12 @@ msgstr "неправилен аргумент към „%s“"
msgid "invalid regex given to -I: '%s'"
msgstr "неправилен регулярен израз подаден към „-I“: „%s“"
+msgid "-G requires a non-empty argument"
+msgstr "опцията „-G“ изисква аргумент"
+
+msgid "-S requires a non-empty argument"
+msgstr "опцията „-S“ изисква аргумент"
+
#, c-format
msgid "failed to parse --submodule option parameter: '%s'"
msgstr "неразпознат параметър към опцията „--submodule“: „%s“"
@@ -19123,6 +19164,10 @@ msgid "unable to write file %s"
msgstr "файлът „%s“ не може да бъде записан"
#, c-format
+msgid "unable to write repeatedly vanishing file %s"
+msgstr "смаляващият се файл „%s“ не може да бъде записван"
+
+#, c-format
msgid "unable to set permission to '%s'"
msgstr "права̀та за достъп до „%s“ не може да бъдат зададени"
@@ -19722,6 +19767,52 @@ msgstr "непознат флаг „%c“"
msgid "unknown non-ascii option in string: `%s'"
msgstr "непозната стойност извън „ascii“ в низа: „%s“"
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the long form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[=<%s>]"
+msgstr "[=%s]"
+
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the short form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[<%s>]"
+msgstr "[%s]"
+
+#. TRANSLATORS: The "<%s>" part of this string stands for a
+#. value given to a command line option, and "<>" is there
+#. as a convention to signal that it is a placeholder
+#. (i.e. the user should substitute it with the real value).
+#. If your language uses a different convention, you can
+#. change "<%s>" part to match yours, e.g. it might use
+#. "|%s|" instead, or if the alphabet is different enough it
+#. may use "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid " <%s>"
+msgstr " %s"
+
msgid "..."
msgstr "…"
@@ -19809,6 +19900,21 @@ msgid "failed to parse %s"
msgstr "„%s“ не може да бъде анализиран"
#, c-format
+msgid "failed to walk children of tree %s: not found"
+msgstr "неуспешно обхождане на дъщерните елементи на дървото „%s“: то липсва"
+
+#, c-format
+msgid "failed to find object %s"
+msgstr "обектът „%s“ липсва"
+
+#, c-format
+msgid "failed to find tag %s"
+msgstr "етикетът „%s“ липсва"
+
+msgid "failed to setup revision walk"
+msgstr "неуспешно настройване на обхождането на версиите"
+
+#, c-format
msgid "Could not make %s writable by group"
msgstr "Не може да се дадат права̀ за запис в директорията „%s“ на групата"
@@ -19957,6 +20063,22 @@ msgstr ""
msgid "could not fetch %s from promisor remote"
msgstr "„%s“ не може да се достави от гарантиращото хранилище"
+#, c-format
+msgid "known remote named '%s' but with url '%s' instead of '%s'"
+msgstr "има хранилище с име „%s“, но адресът му сочи към „%s“, а не към „%s“"
+
+#, c-format
+msgid "unknown '%s' value for '%s' config option"
+msgstr "непозната стойност „%s“ за настройката „%s“"
+
+#, c-format
+msgid "unknown element '%s' from remote info"
+msgstr "непознат елемент „%s“ в информацията за отдалеченото хранилище"
+
+#, c-format
+msgid "accepted promisor remote '%s' not found"
+msgstr "липсва приетото вече хранилище-гарант „%s“"
+
msgid "object-info: expected flush after arguments"
msgstr "object-info: след аргументите се очаква изчистване на буферите"
@@ -20803,6 +20925,14 @@ msgid "invalid refspec '%s'"
msgstr "неправилен указател: „%s“"
#, c-format
+msgid "pattern '%s' has no '*'"
+msgstr "шаблонът „%s“ не съдържа „*“"
+
+#, c-format
+msgid "replacement '%s' has no '*'"
+msgstr "заместителят „%s“ не съдържа „*“"
+
+#, c-format
msgid "invalid quoting in push-option value: '%s'"
msgstr ""
"неправилно екраниране или цитиране в стойността към опция за изтласкване: "
@@ -20930,6 +21060,28 @@ msgid "remote-curl: unknown command '%s' from git"
msgstr "remote-curl: непозната команда „%s“ от git"
#, c-format
+msgid ""
+"reading remote from \"%s/%s\", which is nominated for removal.\n"
+"\n"
+"If you still use the \"remotes/\" directory it is recommended to\n"
+"migrate to config-based remotes:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"If you cannot, please let us know why you still need to use it by\n"
+"sending an e-mail to <git@vger.kernel.org>."
+msgstr ""
+"изчитането на отдалеченото хранилище от „%s/%s“ предстои да бъде "
+"премахнато.\n"
+"Ако все още ползвате директорията „remotes/“, препоръчваме да я мигрирате\n"
+"към следящи директории на база настройки чрез командата:\n"
+"\n"
+" git remote rename %s %s\n"
+"\n"
+"Ако не може поради някаква причина, молим да ни известите за нея с\n"
+"е-писмо до пощенския списък: <git@vger.kernel.org>."
+
+#, c-format
msgid "config remote shorthand cannot begin with '/': %s"
msgstr ""
"съкращението за отдалечено хранилище не може за започва със знака „/“: %s"
@@ -20965,14 +21117,6 @@ msgid "%s tracks both %s and %s"
msgstr "„%s“ следи както „%s“, така и „%s“"
#, c-format
-msgid "key '%s' of pattern had no '*'"
-msgstr "ключ „%s“ на шаблона не съдържа „*“"
-
-#, c-format
-msgid "value '%s' of pattern has no '*'"
-msgstr "стойност „%s“ на шаблона не съдържа „*“"
-
-#, c-format
msgid "src refspec %s does not match any"
msgstr "указателят на версия-източник „%s“ не съвпада с никой обект"
@@ -22945,6 +23089,28 @@ msgstr ""
"какъв брой записи в кеша на обектите-дървета да се отбележат като невалидни "
"(стандартно е 0)"
+msgid "test-tool path-walk <options> -- <revision-options>"
+msgstr "test-tool path-walk ОПЦИЯ… -- ОПЦИЯ_ЗА_ВЕРСИИ…"
+
+msgid "toggle inclusion of blob objects"
+msgstr "превключване на поместването на обекти-BLOB"
+
+msgid "toggle inclusion of commit objects"
+msgstr "превключване на поместването на обекти-подавания"
+
+msgid "toggle inclusion of tag objects"
+msgstr "превключване на поместването на обекти-етикети"
+
+msgid "toggle inclusion of tree objects"
+msgstr "превключване на поместването на обекти-дърво"
+
+msgid "toggle pruning of uninteresting paths"
+msgstr ""
+"превключване на окастрянето на пътищата, които не представляват интерес"
+
+msgid "read a pattern list over stdin"
+msgstr "изчитане на списък с шаблони от стандартния вход"
+
#, c-format
msgid "commit %s is not marked reachable"
msgstr "подаването „%s“ не е отбелязано като достижимо"
@@ -23581,6 +23747,10 @@ msgstr "грешка: "
msgid "warning: "
msgstr "предупреждение: "
+#, c-format
+msgid "uname() failed with error '%s' (%d)\n"
+msgstr "грешка при изпълнението на „uname()“ — „%s“ (%d)\n"
+
msgid "Fetching objects"
msgstr "Доставяне на обектите"
diff --git a/po/de.po b/po/de.po
index addd5919bd..4d9c46fb19 100644
--- 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: 2024-12-20 17:44+0100\n"
-"PO-Revision-Date: 2024-12-27 16:43+0100\n"
+"POT-Creation-Date: 2025-03-06 18:29+0100\n"
+"PO-Revision-Date: 2025-03-07 17:28+0100\n"
"Last-Translator: Ralf Thielow <ralf.thielow@gmail.com>\n"
"Language-Team: German\n"
"Language: de\n"
@@ -2400,6 +2400,18 @@ msgstr "git archive: Protokollfehler"
msgid "git archive: expected a flush"
msgstr "git archive: erwartete eine Spülung (flush)"
+msgid "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+msgstr "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+
+msgid "problem loading sparse-checkout"
+msgstr "Problem beim Laden von sparse-checkout"
+
+msgid "Minimum number of objects to request at a time"
+msgstr "Mindestanzahl der gleichzeitig anzufordernden Objekte"
+
+msgid "Restrict the missing objects to the current sparse-checkout"
+msgstr "fehlenden Objekte im aktuellen sparse-checkout beschränken"
+
msgid ""
"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>] [--no-"
"checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]"
@@ -3167,10 +3179,6 @@ msgstr ""
msgid "git version:\n"
msgstr "git Version:\n"
-#, c-format
-msgid "uname() failed with error '%s' (%d)\n"
-msgstr "uname() ist fehlgeschlagen mit Fehler '%s' (%d)\n"
-
msgid "compiler info: "
msgstr "Compiler Info: "
@@ -4201,8 +4209,95 @@ msgstr ""
"clean.requireForce ist 'true' und -f ist nicht angegeben: clean wird "
"verweigert"
-msgid "git clone [<options>] [--] <repo> [<dir>]"
-msgstr "git clone [<Optionen>] [--] <Repository> [<Verzeichnis>]"
+#, c-format
+msgid "info: Could not add alternate for '%s': %s\n"
+msgstr "info: Konnte Alternative für '%s' nicht hinzufügen: %s\n"
+
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "Konnte '%s' nicht lesen"
+
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s existiert und ist kein Verzeichnis"
+
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr ""
+"'%s' ist eine symbolische Verknüpfung, verweigere das Klonen mit --local"
+
+#, c-format
+msgid "failed to start iterator over '%s'"
+msgstr "Fehler beim Starten der Iteration über '%s'"
+
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr ""
+"symbolische Verknüpfung '%s' existiert, verweigere das Klonen mit --local"
+
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "Konnte '%s' nicht entfernen."
+
+#, c-format
+msgid "hardlink cannot be checked at '%s'"
+msgstr "Hardlink bei '%s' kann nicht geprüft werden"
+
+#, c-format
+msgid "hardlink different from source at '%s'"
+msgstr "Hardlink unterscheidet sich von der Quelle bei '%s'"
+
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "Konnte Verweis '%s' nicht erstellen"
+
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "Konnte Datei nicht nach '%s' kopieren"
+
+#, c-format
+msgid "failed to iterate over '%s'"
+msgstr "Fehler beim Iterieren über '%s'"
+
+#, c-format
+msgid "done.\n"
+msgstr "Fertig.\n"
+
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry with 'git restore --source=HEAD :/'\n"
+msgstr ""
+"Klonen erfolgreich, Auschecken ist aber fehlgeschlagen.\n"
+"Sie können mit 'git status' prüfen, was ausgecheckt worden ist\n"
+"und das Auschecken mit 'git restore --source=HEAD :/' erneut versuchen.\n"
+
+msgid "remote did not send all necessary objects"
+msgstr "Remote-Repository hat nicht alle erforderlichen Objekte gesendet"
+
+#, c-format
+msgid "unable to update %s"
+msgstr "kann %s nicht aktualisieren"
+
+msgid "failed to initialize sparse-checkout"
+msgstr "Fehler beim Initialisieren vom partiellen Checkout."
+
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
+msgstr ""
+"HEAD des Remote-Repositories verweist auf nicht existierende Referenz, kann "
+"nicht ausgecheckt werden"
+
+msgid "unable to checkout working tree"
+msgstr "Arbeitsverzeichnis konnte nicht ausgecheckt werden"
+
+msgid "unable to write parameters to config file"
+msgstr "konnte Parameter nicht in Konfigurationsdatei schreiben"
+
+msgid "cannot repack to clean up"
+msgstr "Kann \"repack\" zum Aufräumen nicht aufrufen"
+
+msgid "cannot unlink temporary alternates file"
+msgstr "Kann temporäre \"alternates\"-Datei nicht entfernen"
msgid "don't clone shallow repository"
msgstr "Repository mit unvollständiger Historie nicht klonen"
@@ -4255,6 +4350,9 @@ msgstr "<Name> statt 'origin' für Upstream-Repository verwenden"
msgid "checkout <branch> instead of the remote's HEAD"
msgstr "<Branch> auschecken, anstatt HEAD des Remote-Repositories"
+msgid "clone single revision <rev> and check out"
+msgstr "einzelnen Commit <Commit> klonen und auschecken"
+
msgid "path to git-upload-pack on the remote"
msgstr "Pfad zu \"git-upload-pack\" auf der Gegenseite"
@@ -4280,8 +4378,8 @@ msgstr "Historie eines flachen Klons vertiefen, Referenz exkludiert"
msgid "clone only one branch, HEAD or --branch"
msgstr "nur einen Branch klonen, HEAD oder --branch"
-msgid "don't clone any tags, and make later fetches not to follow them"
-msgstr "keine Tags klonen, und auch bei späteren Abrufen nicht beachten"
+msgid "clone tags, and make later fetches not to follow them"
+msgstr "Tags klonen und dafür sorgen, dass spätere Abrufe ihnen nicht folgen"
msgid "any cloned submodules will be shallow"
msgstr "jedes geklonte Submodul mit unvollständiger Historie (shallow)"
@@ -4326,99 +4424,8 @@ msgstr ""
"eine URI für das Herunterladen von Bundles vor dem Abruf\n"
"aus dem ursprünglichen Remote-Repository"
-#, c-format
-msgid "info: Could not add alternate for '%s': %s\n"
-msgstr "info: Konnte Alternative für '%s' nicht hinzufügen: %s\n"
-
-#, c-format
-msgid "failed to stat '%s'"
-msgstr "Konnte '%s' nicht lesen"
-
-#, c-format
-msgid "%s exists and is not a directory"
-msgstr "%s existiert und ist kein Verzeichnis"
-
-#, c-format
-msgid "'%s' is a symlink, refusing to clone with --local"
-msgstr ""
-"'%s' ist eine symbolische Verknüpfung, verweigere das Klonen mit --local"
-
-#, c-format
-msgid "failed to start iterator over '%s'"
-msgstr "Fehler beim Starten der Iteration über '%s'"
-
-#, c-format
-msgid "symlink '%s' exists, refusing to clone with --local"
-msgstr ""
-"symbolische Verknüpfung '%s' existiert, verweigere das Klonen mit --local"
-
-#, c-format
-msgid "failed to unlink '%s'"
-msgstr "Konnte '%s' nicht entfernen."
-
-#, c-format
-msgid "hardlink cannot be checked at '%s'"
-msgstr "Hardlink bei '%s' kann nicht geprüft werden"
-
-#, c-format
-msgid "hardlink different from source at '%s'"
-msgstr "Hardlink unterscheidet sich von der Quelle bei '%s'"
-
-#, c-format
-msgid "failed to create link '%s'"
-msgstr "Konnte Verweis '%s' nicht erstellen"
-
-#, c-format
-msgid "failed to copy file to '%s'"
-msgstr "Konnte Datei nicht nach '%s' kopieren"
-
-#, c-format
-msgid "failed to iterate over '%s'"
-msgstr "Fehler beim Iterieren über '%s'"
-
-#, c-format
-msgid "done.\n"
-msgstr "Fertig.\n"
-
-msgid ""
-"Clone succeeded, but checkout failed.\n"
-"You can inspect what was checked out with 'git status'\n"
-"and retry with 'git restore --source=HEAD :/'\n"
-msgstr ""
-"Klonen erfolgreich, Auschecken ist aber fehlgeschlagen.\n"
-"Sie können mit 'git status' prüfen, was ausgecheckt worden ist\n"
-"und das Auschecken mit 'git restore --source=HEAD :/' erneut versuchen.\n"
-
-#, c-format
-msgid "Could not find remote branch %s to clone."
-msgstr "Konnte zu klonenden Remote-Branch %s nicht finden."
-
-msgid "remote did not send all necessary objects"
-msgstr "Remote-Repository hat nicht alle erforderlichen Objekte gesendet"
-
-#, c-format
-msgid "unable to update %s"
-msgstr "kann %s nicht aktualisieren"
-
-msgid "failed to initialize sparse-checkout"
-msgstr "Fehler beim Initialisieren vom partiellen Checkout."
-
-msgid "remote HEAD refers to nonexistent ref, unable to checkout"
-msgstr ""
-"HEAD des Remote-Repositories verweist auf nicht existierende Referenz, kann "
-"nicht ausgecheckt werden"
-
-msgid "unable to checkout working tree"
-msgstr "Arbeitsverzeichnis konnte nicht ausgecheckt werden"
-
-msgid "unable to write parameters to config file"
-msgstr "konnte Parameter nicht in Konfigurationsdatei schreiben"
-
-msgid "cannot repack to clean up"
-msgstr "Kann \"repack\" zum Aufräumen nicht aufrufen"
-
-msgid "cannot unlink temporary alternates file"
-msgstr "Kann temporäre \"alternates\"-Datei nicht entfernen"
+msgid "git clone [<options>] [--] <repo> [<dir>]"
+msgstr "git clone [<Optionen>] [--] <Repository> [<Verzeichnis>]"
msgid "Too many arguments."
msgstr "Zu viele Argumente."
@@ -4531,6 +4538,10 @@ msgstr "Remoteübertragung meldete Fehler"
msgid "Remote branch %s not found in upstream %s"
msgstr "Remote-Branch %s nicht im Upstream-Repository %s gefunden"
+#, c-format
+msgid "Remote revision %s not found in upstream %s"
+msgstr "Remote-Commit %s nicht im Upstream-Repository %s gefunden"
+
msgid "You appear to have cloned an empty repository."
msgstr "Sie scheinen ein leeres Repository geklont zu haben."
@@ -4710,7 +4721,7 @@ msgid "git commit-tree: failed to read"
msgstr "git commit-tree: Fehler beim Lesen"
msgid ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
"reword):]<commit>]\n"
" [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
@@ -4720,14 +4731,14 @@ msgid ""
" [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
" [--] [<pathspec>...]"
msgstr ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<Modus>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<Modus>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <Commit> | --fixup [(amend|"
"reword):]<Commit>]\n"
" [-F <Datei> | -m <Nachricht>] [--reset-author] [--allow-empty]\n"
" [--allow-empty-message] [--no-verify] [-e] [--author=<Autor>]\n"
" [--date=<Datum>] [--cleanup=<Modus>] [--[no-]status]\n"
" [-i | -o] [--pathspec-from-file=<Datei> [--pathspec-file-nul]]\n"
-" [(--trailer <Token>[(=|:)<Wert>])...] [-S[<Key-Id>]]\n"
+" [(--trailer <Token>[(=|:)<Wert>])...] [-S[<Key-ID>>]]\n"
" [--] [<Pfadspezifikation>...]"
msgid "git status [<options>] [--] [<pathspec>...]"
@@ -6139,14 +6150,15 @@ msgid ""
"Run 'git remote set-head %s %s' to follow the change, or set\n"
"'remote.%s.followRemoteHEAD' configuration option to a different value\n"
"if you do not want to see this message. Specifically running\n"
-"'git config set remote.%s.followRemoteHEAD %s' will disable the warning\n"
-"until the remote changes HEAD to something else."
+"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n"
+"will disable the warning until the remote changes HEAD to something else."
msgstr ""
"Führen Sie 'git remote set-head %s %s' aus, um der Änderung zu folgen,\n"
"oder setzen Sie die Konfiguration 'remote.%s.followRemoteHEAD' auf einen\n"
-"anderen Wert, wenn Sie diese Meldung nicht sehen wollen. Konkret wird diese\n"
-"Warnung mit 'git config set remote.%s.followRemoteHEAD %s' deaktiviert\n"
-"bis die Gegenstelle HEAD in etwas anderes ändert."
+"anderen Wert wenn Sie diese Meldung nicht sehen wollen. Speziell der Befehl\n"
+"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n"
+"wird die Warnung deaktivieren, bis das Remote-Repository HEAD in etwas\n"
+"anderes ändert."
msgid "multiple branches detected, incompatible with --set-upstream"
msgstr "mehrere Branches erkannt, inkompatibel mit --set-upstream"
@@ -6842,6 +6854,9 @@ msgstr ""
msgid "repack all other packs except the largest pack"
msgstr "alle anderen Pakete, außer das größte Paket, neu packen"
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr "pack-Präfix zum Speichern eines Pakets mit gelöschten Objekten"
+
#, c-format
msgid "failed to parse gc.logExpiry value %s"
msgstr "Fehler beim Parsen des Wertes '%s' von gc.logExpiry"
@@ -7659,6 +7674,10 @@ msgid "Cannot come back to cwd"
msgstr "Kann nicht zurück zum Arbeitsverzeichnis wechseln"
#, c-format
+msgid "bad --pack_header: %s"
+msgstr "ungültiger --pack_header: %s"
+
+#, c-format
msgid "bad %s"
msgstr "%s ist ungültig"
@@ -8537,11 +8556,6 @@ msgstr "unbekannte Strategie-Option: -X%s"
msgid "malformed input line: '%s'."
msgstr "Fehlerhafte Eingabezeile: '%s'."
-#, c-format
-msgid "merging cannot continue; got unclean result of %d"
-msgstr ""
-"Merge kann nicht fortgesetzt werden; unsauberes Ergebnis von %d erhalten"
-
msgid "git merge [<options>] [<commit>...]"
msgstr "git merge [<Optionen>] [<Commit>...]"
@@ -9382,6 +9396,13 @@ msgstr ""
"<Objektliste>]"
#, c-format
+msgid "invalid --name-hash-version option: %d"
+msgstr "ungültige Option für --name-hash-version: %d"
+
+msgid "currently, --write-bitmap-index requires --name-hash-version=1"
+msgstr "derzeit erfordert --write-bitmap-index --name-hash-version=1"
+
+#, c-format
msgid ""
"write_reuse_object: could not locate %s, expected at offset %<PRIuMAX> in "
"pack %s"
@@ -9718,6 +9739,11 @@ msgstr ""
"jegliche konfigurierte uploadpack.blobpackfileuri für dieses Protkoll "
"ausschließen"
+msgid "use the specified name-hash function to group similar objects"
+msgstr ""
+"die angegebene Name-Hash-Funktion verwenden, um ähnliche Objekte zu "
+"gruppieren"
+
#, c-format
msgid "delta chain depth %d is too deep, forcing %d"
msgstr "Tiefe für Verkettung von Unterschieden %d ist zu tief, erzwinge %d"
@@ -11006,8 +11032,8 @@ msgstr "Kein Reflog zum Löschen angegeben."
msgid "invalid ref format: %s"
msgstr "Ungültiges Format für Referenzen: %s"
-msgid "git refs migrate --ref-format=<format> [--dry-run]"
-msgstr "git refs migrate --ref-format=<Format> [--dry-run]"
+msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
+msgstr "git refs migrate --ref-format=<Format> [--no-reflog] [--dry-run]"
msgid "git refs verify [--strict] [--verbose]"
msgstr "git refs verify [--strict] [--verbose]"
@@ -11018,6 +11044,9 @@ msgstr "das Referenzformat angeben, in das konvertiert werden soll"
msgid "perform a non-destructive dry-run"
msgstr "einen zerstörungsfreien Trockenlauf durchführen"
+msgid "drop reflogs entirely during the migration"
+msgstr "Reflogs während der Migration vollständig zu löschen"
+
msgid "missing --ref-format=<format>"
msgstr "fehlendes --ref-format=<Format>"
@@ -11488,8 +11517,14 @@ msgstr "Werde keine URLs entfernen, die nicht für \"push\" bestimmt sind"
msgid "be verbose; must be placed before a subcommand"
msgstr "erweiterte Ausgaben; muss vor einem Unterbefehl angegeben werden"
-msgid "git repack [<options>]"
-msgstr "git repack [<Optionen>]"
+msgid ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
+msgstr ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<Paketname>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
msgid ""
"Incremental repacks are incompatible with bitmap indexes. Use\n"
@@ -11568,6 +11603,12 @@ msgstr "--no-reuse-delta an git-pack-objects übergeben"
msgid "pass --no-reuse-object to git-pack-objects"
msgstr "--no-reuse-object an git-pack-objects übergeben"
+msgid ""
+"specify the name hash version to use for grouping similar objects by path"
+msgstr ""
+"die Version des Hash-Namens angeben, die für die Gruppierung ähnlicher "
+"Objekte nach Pfad verwendet werden soll"
+
msgid "do not run git-update-server-info"
msgstr "git-update-server-info nicht ausführen"
@@ -11619,9 +11660,6 @@ msgstr "eine geometrische Progression mit Faktor <N> finden"
msgid "write a multi-pack index of the resulting packs"
msgstr "ein Multi-Pack-Index des resultierenden Pakets schreiben"
-msgid "pack prefix to store a pack containing pruned objects"
-msgstr "pack-Präfix zum Speichern eines Pakets mit gelöschten Objekten"
-
msgid "pack prefix to store a pack containing filtered out objects"
msgstr "Paketpräfix, um ein Paket mit herausgefilterten Objekten zu speichern"
@@ -11839,9 +11877,6 @@ 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"
@@ -14571,6 +14606,9 @@ msgstr "ein GNU Arch Repository in Git importieren"
msgid "Create an archive of files from a named tree"
msgstr "Dateiarchiv von angegebenem Verzeichnis erstellen"
+msgid "Download missing objects in a partial clone"
+msgstr "fehlender Objekte in einem partiellen Klon herunterladen"
+
msgid "Use binary search to find the commit that introduced a bug"
msgstr ""
"Binärsuche verwenden, um den Commit zu finden, der einen Fehler verursacht "
@@ -16543,6 +16581,12 @@ msgstr "ungültiges Argument für %s"
msgid "invalid regex given to -I: '%s'"
msgstr "ungültiger regulärer Ausdruck für -I gegeben: '%s'"
+msgid "-G requires a non-empty argument"
+msgstr "-G erfordert ein nicht leeres Argument"
+
+msgid "-S requires a non-empty argument"
+msgstr "-S erfordert ein nicht leeres Argument"
+
#, c-format
msgid "failed to parse --submodule option parameter: '%s'"
msgstr "Fehler beim Parsen des --submodule Optionsparameters: '%s'"
@@ -18782,6 +18826,10 @@ msgid "unable to write file %s"
msgstr "Konnte Datei %s nicht schreiben."
#, c-format
+msgid "unable to write repeatedly vanishing file %s"
+msgstr "konnte nicht in eine wiederholt verschwindende Datei %s schreiben"
+
+#, c-format
msgid "unable to set permission to '%s'"
msgstr "Konnte Zugriffsberechtigung auf '%s' nicht setzen."
@@ -19355,6 +19403,52 @@ msgstr "Unbekannter Schalter `%c'"
msgid "unknown non-ascii option in string: `%s'"
msgstr "Unbekannte nicht-Ascii Option in String: `%s'"
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the long form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[=<%s>]"
+msgstr "[=<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the short form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[<%s>]"
+msgstr "[<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string stands for a
+#. value given to a command line option, and "<>" is there
+#. as a convention to signal that it is a placeholder
+#. (i.e. the user should substitute it with the real value).
+#. If your language uses a different convention, you can
+#. change "<%s>" part to match yours, e.g. it might use
+#. "|%s|" instead, or if the alphabet is different enough it
+#. may use "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid " <%s>"
+msgstr " <%s>"
+
msgid "..."
msgstr "..."
@@ -19442,6 +19536,21 @@ msgid "failed to parse %s"
msgstr "Fehler beim Parsen von %s."
#, c-format
+msgid "failed to walk children of tree %s: not found"
+msgstr "Fehlen beim Durchlaufen der Kinder von Baum %s: nicht gefunden"
+
+#, c-format
+msgid "failed to find object %s"
+msgstr "Objekt nicht gefunden: %s"
+
+#, c-format
+msgid "failed to find tag %s"
+msgstr "Tag nicht gefunden: %s"
+
+msgid "failed to setup revision walk"
+msgstr "Einrichtung des Revisionsgangs fehlgeschlagen"
+
+#, c-format
msgid "Could not make %s writable by group"
msgstr "Konnte Gruppenschreibrecht für %s nicht setzen."
@@ -19591,6 +19700,25 @@ msgstr "Promisor-Remote-Name kann nicht mit '/' beginnen: %s"
msgid "could not fetch %s from promisor remote"
msgstr "konnte %s nicht von Promisor-Remote abrufen"
+#, c-format
+msgid "known remote named '%s' but with url '%s' instead of '%s'"
+msgstr ""
+"bekanntes Remote-Repository mit dem Namen '%s', aber mit der URL '%s' statt "
+"'%s'"
+
+#, c-format
+msgid "unknown '%s' value for '%s' config option"
+msgstr "unbekannter '%s'-Wert für '%s'-Konfigurationsoption"
+
+#, c-format
+msgid "unknown element '%s' from remote info"
+msgstr "unbekanntes Element '%s' aus Information aus Remote-Repository"
+
+#, c-format
+msgid "accepted promisor remote '%s' not found"
+msgstr ""
+"akzeptiertes partiell geklontes \"Remote-Repository '%s' nicht gefunden"
+
msgid "object-info: expected flush after arguments"
msgstr "object-info: erwartete Flush nach Argumenten"
@@ -20428,6 +20556,14 @@ msgid "invalid refspec '%s'"
msgstr "ungültige Refspec '%s'"
#, c-format
+msgid "pattern '%s' has no '*'"
+msgstr "Muster '%s' hat keinen '*'"
+
+#, c-format
+msgid "replacement '%s' has no '*'"
+msgstr "Ersetzung '%s' hat keinen '*'"
+
+#, c-format
msgid "invalid quoting in push-option value: '%s'"
msgstr "Ungültiges Quoting beim \"push-option\"-Wert: '%s'"
@@ -20550,6 +20686,28 @@ msgid "remote-curl: unknown command '%s' from git"
msgstr "remote-curl: Unbekannter Befehl '%s' von Git"
#, c-format
+msgid ""
+"reading remote from \"%s/%s\", which is nominated for removal.\n"
+"\n"
+"If you still use the \"remotes/\" directory it is recommended to\n"
+"migrate to config-based remotes:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"If you cannot, please let us know why you still need to use it by\n"
+"sending an e-mail to <git@vger.kernel.org>."
+msgstr ""
+"Lese von Remote-Repository \"%s/%s\", das zum Entfernen vorgeschlagen wird.\n"
+"\n"
+"Wenn Sie noch das Verzeichnis \"remotes/\" verwenden, wird empfohlen\n"
+"auf konfigurationsbasierte Remote-Repositories umzusteigen:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"Wenn dies nicht möglich ist, teilen Sie uns bitte mit, warum Sie das noch\n"
+"verwenden müssen, indem Sie eine E-Mail an <git@vger.kernel.org> senden."
+
+#, c-format
msgid "config remote shorthand cannot begin with '/': %s"
msgstr ""
"Kürzel für Remote-Repository in der Konfiguration kann nicht mit '/' "
@@ -20586,14 +20744,6 @@ msgid "%s tracks both %s and %s"
msgstr "%s folgt sowohl %s als auch %s"
#, c-format
-msgid "key '%s' of pattern had no '*'"
-msgstr "Schlüssel '%s' des Musters hatte kein '*'."
-
-#, c-format
-msgid "value '%s' of pattern has no '*'"
-msgstr "Wert '%s' des Musters hat kein '*'."
-
-#, c-format
msgid "src refspec %s does not match any"
msgstr "Src-Refspec %s entspricht keiner Referenz."
@@ -22519,6 +22669,27 @@ msgstr ""
"Anzahl der Einträge im Cache-Verzeichnis, die ungültig gemacht werden sollen "
"(Standardwert 0)"
+msgid "test-tool path-walk <options> -- <revision-options>"
+msgstr "test-tool path-walk <Optionen> -- <Commit-Optionen>"
+
+msgid "toggle inclusion of blob objects"
+msgstr "Einbeziehung von Blob-Objekten umschalten"
+
+msgid "toggle inclusion of commit objects"
+msgstr "Einbeziehung von Commit-Objekten umschalten"
+
+msgid "toggle inclusion of tag objects"
+msgstr "Einbeziehung von Tag-Objekten umschalten"
+
+msgid "toggle inclusion of tree objects"
+msgstr "Einbeziehung von Tree-Objekten umschalten"
+
+msgid "toggle pruning of uninteresting paths"
+msgstr "Auslassen von uninteressanten Pfaden umschalten"
+
+msgid "read a pattern list over stdin"
+msgstr "Liste von Mustern von der Standard-Eingabe lesen"
+
#, c-format
msgid "commit %s is not marked reachable"
msgstr "Commit %s ist nicht als erreichbar gekennzeichnet."
@@ -23168,6 +23339,10 @@ msgstr "Fehler: "
msgid "warning: "
msgstr "Warnung: "
+#, c-format
+msgid "uname() failed with error '%s' (%d)\n"
+msgstr "uname() ist fehlgeschlagen mit Fehler '%s' (%d)\n"
+
msgid "Fetching objects"
msgstr "Anfordern der Objekte"
@@ -24190,7 +24365,3 @@ msgstr "Lasse %s mit Backup-Suffix '%s' aus.\n"
#, perl-format
msgid "Do you really want to send %s? [y|N]: "
msgstr "Wollen Sie %s wirklich versenden? [y|N]: "
-
-#, c-format
-#~ msgid "preferred pack (%s) is invalid"
-#~ msgstr "bevorzugtes Paket (%s) ist ungültig"
diff --git a/po/fr.po b/po/fr.po
index 64a75aecf5..8e33390307 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -87,8 +87,8 @@ msgid ""
msgstr ""
"Project-Id-Version: git\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2024-12-23 18:57+0000\n"
-"PO-Revision-Date: 2024-12-29 18:26+0100\n"
+"POT-Creation-Date: 2025-03-05 22:57+0000\n"
+"PO-Revision-Date: 2025-03-06 16:46+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"
@@ -2452,6 +2452,18 @@ msgstr "git archive : erreur de protocole"
msgid "git archive: expected a flush"
msgstr "git archive : vidage attendu"
+msgid "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+msgstr "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+
+msgid "problem loading sparse-checkout"
+msgstr "problème lors du chargement de l'extraction clairsemée"
+
+msgid "Minimum number of objects to request at a time"
+msgstr "Nombre minimum d'objets à demander à chaque fois"
+
+msgid "Restrict the missing objects to the current sparse-checkout"
+msgstr "Restreindre les objets manquants à l'extraction clairsemée actuelle"
+
msgid ""
"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>] [--no-"
"checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]"
@@ -3217,10 +3229,6 @@ msgstr ""
msgid "git version:\n"
msgstr "version git ::\n"
-#, c-format
-msgid "uname() failed with error '%s' (%d)\n"
-msgstr "échec de uname() avec l'erreur '%s' (%d)\n"
-
msgid "compiler info: "
msgstr "info compilateur : "
@@ -4243,8 +4251,93 @@ msgid "clean.requireForce is true and -f not given: refusing to clean"
msgstr ""
"clean.requireForce positionné est true et -f non fourni ; refus de nettoyer"
-msgid "git clone [<options>] [--] <repo> [<dir>]"
-msgstr "git clone [<options>] [--] <dépôt> [<répertoire>]"
+#, c-format
+msgid "info: Could not add alternate for '%s': %s\n"
+msgstr "info : impossible d'ajouter une alternative pour '%s' : %s\n"
+
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "échec du stat de '%s'"
+
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s existe et n'est pas un répertoire"
+
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "'%s' est un lien symbolique, refus de cloner avec --local"
+
+#, c-format
+msgid "failed to start iterator over '%s'"
+msgstr "échec du démarrage un itérateur sur '%s'"
+
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr "le lien symbolique '%s' existe, refus de cloner avec --local"
+
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "échec pour délier '%s'"
+
+#, c-format
+msgid "hardlink cannot be checked at '%s'"
+msgstr "le lien dur ne peut pas être vérifié à '%s'"
+
+#, c-format
+msgid "hardlink different from source at '%s'"
+msgstr "le lien dur est différent de la source à '%s'"
+
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "échec de la création du lien '%s'"
+
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "échec de la copie vers '%s'"
+
+#, c-format
+msgid "failed to iterate over '%s'"
+msgstr "échec de l'itération sur '%s'"
+
+#, c-format
+msgid "done.\n"
+msgstr "fait.\n"
+
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry with 'git restore --source=HEAD :/'\n"
+msgstr ""
+"Le clone a réussi, mais l'extraction a échoué.\n"
+"Vous pouvez inspecter ce qui a été extrait avec 'git status'\n"
+"et réessayer avec 'git restore --source=HEAD :/'\n"
+
+msgid "remote did not send all necessary objects"
+msgstr "le serveur distant n'a pas envoyé tous les objets nécessaires"
+
+#, c-format
+msgid "unable to update %s"
+msgstr "impossible de mettre à jour %s"
+
+msgid "failed to initialize sparse-checkout"
+msgstr "échec lors de l'initialisation l'extraction clairsemée"
+
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
+msgstr ""
+"la HEAD distante réfère à une référence non existante, impossible de "
+"l'extraire"
+
+msgid "unable to checkout working tree"
+msgstr "impossible d'extraire la copie de travail"
+
+msgid "unable to write parameters to config file"
+msgstr "impossible d'écrire les paramètres dans le fichier de configuration"
+
+msgid "cannot repack to clean up"
+msgstr "impossible de remballer pour nettoyer"
+
+msgid "cannot unlink temporary alternates file"
+msgstr "impossible de délier le fichier temporaire alternates"
msgid "don't clone shallow repository"
msgstr "ne pas cloner un dépôt superficiel"
@@ -4297,6 +4390,9 @@ msgstr "utiliser <nom> au lieu de 'origin' pour suivre la branche amont"
msgid "checkout <branch> instead of the remote's HEAD"
msgstr "extraire <branche> au lieu de la HEAD du répertoire distant"
+msgid "clone single revision <rev> and check out"
+msgstr "cloner la révision unique <rév> et l'extraire"
+
msgid "path to git-upload-pack on the remote"
msgstr "chemin vers git-upload-pack sur le serveur distant"
@@ -4318,10 +4414,9 @@ msgstr "approfondit l'historique d'un clone superficiel en excluant une ref"
msgid "clone only one branch, HEAD or --branch"
msgstr "cloner seulement une branche, HEAD ou --branch"
-msgid "don't clone any tags, and make later fetches not to follow them"
+msgid "clone tags, and make later fetches not to follow them"
msgstr ""
-"ne pas cloner les tags et indiquer aux récupérations futures de ne pas le "
-"faire"
+"cloner les tags et indiquer aux récupérations futures de ne pas le faire"
msgid "any cloned submodules will be shallow"
msgstr "tous les sous-modules clonés seront superficiels"
@@ -4367,97 +4462,8 @@ msgstr ""
"un URI pour télécharger des paquets avant de récupérer depuis le distant "
"d'origine"
-#, c-format
-msgid "info: Could not add alternate for '%s': %s\n"
-msgstr "info : impossible d'ajouter une alternative pour '%s' : %s\n"
-
-#, c-format
-msgid "failed to stat '%s'"
-msgstr "échec du stat de '%s'"
-
-#, c-format
-msgid "%s exists and is not a directory"
-msgstr "%s existe et n'est pas un répertoire"
-
-#, c-format
-msgid "'%s' is a symlink, refusing to clone with --local"
-msgstr "'%s' est un lien symbolique, refus de cloner avec --local"
-
-#, c-format
-msgid "failed to start iterator over '%s'"
-msgstr "échec du démarrage un itérateur sur '%s'"
-
-#, c-format
-msgid "symlink '%s' exists, refusing to clone with --local"
-msgstr "le lien symbolique '%s' existe, refus de cloner avec --local"
-
-#, c-format
-msgid "failed to unlink '%s'"
-msgstr "échec pour délier '%s'"
-
-#, c-format
-msgid "hardlink cannot be checked at '%s'"
-msgstr "le lien dur ne peut pas être vérifié à '%s'"
-
-#, c-format
-msgid "hardlink different from source at '%s'"
-msgstr "le lien dur est différent de la source à '%s'"
-
-#, c-format
-msgid "failed to create link '%s'"
-msgstr "échec de la création du lien '%s'"
-
-#, c-format
-msgid "failed to copy file to '%s'"
-msgstr "échec de la copie vers '%s'"
-
-#, c-format
-msgid "failed to iterate over '%s'"
-msgstr "échec de l'itération sur '%s'"
-
-#, c-format
-msgid "done.\n"
-msgstr "fait.\n"
-
-msgid ""
-"Clone succeeded, but checkout failed.\n"
-"You can inspect what was checked out with 'git status'\n"
-"and retry with 'git restore --source=HEAD :/'\n"
-msgstr ""
-"Le clone a réussi, mais l'extraction a échoué.\n"
-"Vous pouvez inspecter ce qui a été extrait avec 'git status'\n"
-"et réessayer avec 'git restore --source=HEAD :/'\n"
-
-#, c-format
-msgid "Could not find remote branch %s to clone."
-msgstr "Impossible de trouver la branche distante '%s' à cloner."
-
-msgid "remote did not send all necessary objects"
-msgstr "le serveur distant n'a pas envoyé tous les objets nécessaires"
-
-#, c-format
-msgid "unable to update %s"
-msgstr "impossible de mettre à jour %s"
-
-msgid "failed to initialize sparse-checkout"
-msgstr "échec lors de l'initialisation l'extraction clairsemée"
-
-msgid "remote HEAD refers to nonexistent ref, unable to checkout"
-msgstr ""
-"la HEAD distante réfère à une référence non existante, impossible de "
-"l'extraire"
-
-msgid "unable to checkout working tree"
-msgstr "impossible d'extraire la copie de travail"
-
-msgid "unable to write parameters to config file"
-msgstr "impossible d'écrire les paramètres dans le fichier de configuration"
-
-msgid "cannot repack to clean up"
-msgstr "impossible de remballer pour nettoyer"
-
-msgid "cannot unlink temporary alternates file"
-msgstr "impossible de délier le fichier temporaire alternates"
+msgid "git clone [<options>] [--] <repo> [<dir>]"
+msgstr "git clone [<options>] [--] <dépôt> [<répertoire>]"
msgid "Too many arguments."
msgstr "Trop d'arguments."
@@ -4563,6 +4569,10 @@ msgstr "le transport distant a retourné une erreur"
msgid "Remote branch %s not found in upstream %s"
msgstr "La branche distante %s n'a pas été trouvée dans le dépôt amont %s"
+#, c-format
+msgid "Remote revision %s not found in upstream %s"
+msgstr "La révision distante %s n'a pas été trouvée dans le dépôt amont %s"
+
msgid "You appear to have cloned an empty repository."
msgstr "Vous semblez avoir cloné un dépôt vide."
@@ -4737,7 +4747,7 @@ msgid "git commit-tree: failed to read"
msgstr "git commit-tree : échec de la lecture"
msgid ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
"reword):]<commit>]\n"
" [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
@@ -4747,14 +4757,14 @@ msgid ""
" [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
" [--] [<pathspec>...]"
msgstr ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
-"reword):]<commit>)]\n"
+"reword):]<commit>]\n"
" [-F <fichier> | -m <msg>] [--reset-author] [--allow-empty]\n"
" [--allow-empty-message] [--no-verify] [-e] [--author=<auteur>]\n"
" [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
" [-i | -o] [--pathspec-from-file=<fichier> [--pathspec-file-nul]]\n"
-" [(--trailer <symbole>[(=|:)<valeur>])...] [-S[<id-clé>]]\n"
+" [(--trailer <jeton>[(=|:)<valeur>])...] [-S[<id-clé>]]\n"
" [--] [<spéc-de-chemin>...]"
msgid "git status [<options>] [--] [<pathspec>...]"
@@ -6168,13 +6178,14 @@ msgid ""
"Run 'git remote set-head %s %s' to follow the change, or set\n"
"'remote.%s.followRemoteHEAD' configuration option to a different value\n"
"if you do not want to see this message. Specifically running\n"
-"'git config set remote.%s.followRemoteHEAD %s' will disable the warning\n"
-"until the remote changes HEAD to something else."
+"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n"
+"will disable the warning until the remote changes HEAD to something else."
msgstr ""
"Lancez 'git remote set-head %s %s' pour suivre la modification, ou\n"
"réglez l'option de configuration 'remote.%s.followRemoteHEAD' à une\n"
"valeur différente si vous ne souhaitez pas voir ce message. Lancer\n"
-"spécifiquement 'git config set remote.%s.followRemoteHEAD %s'\n"
+"spécifiquement 'git config set remote.%s.followRemoteHEAD warn-if-not-branch-"
+"%s'\n"
"va désactiver l'alerte jusqu'à ce que le distant change HEAD."
msgid "multiple branches detected, incompatible with --set-upstream"
@@ -6860,6 +6871,9 @@ msgstr ""
msgid "repack all other packs except the largest pack"
msgstr "recompacter tous les autres paquets excepté le plus gros paquet"
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr "préfixe de paquet pour stocker un paquet contenant les objets élagués"
+
#, c-format
msgid "failed to parse gc.logExpiry value %s"
msgstr "impossible d'analyser gc.logExpiry %s"
@@ -7674,6 +7688,10 @@ msgid "Cannot come back to cwd"
msgstr "Impossible de revenir au répertoire de travail courant"
#, c-format
+msgid "bad --pack_header: %s"
+msgstr "mauvais --pack_header : %s"
+
+#, c-format
msgid "bad %s"
msgstr "mauvais %s"
@@ -8561,10 +8579,6 @@ msgstr "option de stratégie inconnue : -X%s"
msgid "malformed input line: '%s'."
msgstr "ligne en entrée malformée : '%s'."
-#, c-format
-msgid "merging cannot continue; got unclean result of %d"
-msgstr "la fusion ne peut pas continuer ; résultat non propre retourné %d"
-
msgid "git merge [<options>] [<commit>...]"
msgstr "git merge [<options>] [<commit>...]"
@@ -9398,6 +9412,13 @@ msgstr ""
"objets>]"
#, c-format
+msgid "invalid --name-hash-version option: %d"
+msgstr "option --name-hash-version invalide : %d"
+
+msgid "currently, --write-bitmap-index requires --name-hash-version=1"
+msgstr "actuellement, --write-bitmap-index nécessite --name-hash-version=1"
+
+#, c-format
msgid ""
"write_reuse_object: could not locate %s, expected at offset %<PRIuMAX> in "
"pack %s"
@@ -9730,6 +9751,11 @@ msgstr "protocole"
msgid "exclude any configured uploadpack.blobpackfileuri with this protocol"
msgstr "exclure tout uploadpack.blobpackfileuri configuré avec ce protocole"
+msgid "use the specified name-hash function to group similar objects"
+msgstr ""
+"utiliser la fonction d'empreinte de nom spécifiée pour grouper les objets "
+"similaires"
+
#, c-format
msgid "delta chain depth %d is too deep, forcing %d"
msgstr "la profondeur %d de chaîne de delta est trop grande, forcée à %d"
@@ -10992,8 +11018,8 @@ msgstr "pas de journal de références à supprimer spécifié"
msgid "invalid ref format: %s"
msgstr "format de référence invalide : %s"
-msgid "git refs migrate --ref-format=<format> [--dry-run]"
-msgstr "git refs migrate --ref-format=<format> [--dry-run]"
+msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
+msgstr "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
msgid "git refs verify [--strict] [--verbose]"
msgstr "git refs verify [--strict] [--verbose]"
@@ -11004,6 +11030,9 @@ msgstr "spécifier le format de réference vers lequel convertir"
msgid "perform a non-destructive dry-run"
msgstr "faire l'action en mode simulé non destructif"
+msgid "drop reflogs entirely during the migration"
+msgstr "abandonner complètement le reflog pendant la migration"
+
msgid "missing --ref-format=<format>"
msgstr "--ref-format=<format> manquant"
@@ -11475,8 +11504,14 @@ msgstr "Pas de suppression de toutes les URLs non-push"
msgid "be verbose; must be placed before a subcommand"
msgstr "être verbeux : doit être placé avant une sous-commande"
-msgid "git repack [<options>]"
-msgstr "git repack [<options>]"
+msgid ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
+msgstr ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<nom-de-paquet>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
msgid ""
"Incremental repacks are incompatible with bitmap indexes. Use\n"
@@ -11557,6 +11592,12 @@ msgstr "passer --no-reuse-delta à git-pack-objects"
msgid "pass --no-reuse-object to git-pack-objects"
msgstr "passer --no-reuse-object à git-pack-objects"
+msgid ""
+"specify the name hash version to use for grouping similar objects by path"
+msgstr ""
+"spécifier la verison d'empreinte de nom à utiliser pour grouper les objets "
+"similaires par chemin"
+
msgid "do not run git-update-server-info"
msgstr "ne pas lancer git-update-server-info"
@@ -11606,9 +11647,6 @@ msgstr "trouver une progression géométrique avec un facteur <N>"
msgid "write a multi-pack index of the resulting packs"
msgstr "écrire un index de multi-paquet des paquets résultants"
-msgid "pack prefix to store a pack containing pruned objects"
-msgstr "préfixe de paquet pour stocker un paquet contenant les objets élagués"
-
msgid "pack prefix to store a pack containing filtered out objects"
msgstr "préfixe de paquet pour stocker un paquet contenant les objets filtrés"
@@ -11825,9 +11863,6 @@ 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"
@@ -14539,6 +14574,9 @@ msgstr "Importer dans Git un dépôt GNU Arch"
msgid "Create an archive of files from a named tree"
msgstr "Créer une archive des fichiers depuis un arbre nommé"
+msgid "Download missing objects in a partial clone"
+msgstr "Télécharger les objets manquants dans un clone partiel"
+
msgid "Use binary search to find the commit that introduced a bug"
msgstr "Trouver par recherche binaire la modification qui a introduit un bogue"
@@ -16531,6 +16569,12 @@ msgstr "argument invalide pour %s"
msgid "invalid regex given to -I: '%s'"
msgstr "regex invalide fournie à -I : '%s'"
+msgid "-G requires a non-empty argument"
+msgstr "-G exige un argument non vide"
+
+msgid "-S requires a non-empty argument"
+msgstr "-S exige un argument non vide"
+
#, c-format
msgid "failed to parse --submodule option parameter: '%s'"
msgstr "échec de l'analyse du paramètre de l'option --submodule : '%s'"
@@ -18767,6 +18811,10 @@ msgid "unable to write file %s"
msgstr "impossible d'écrire le fichier %s"
#, c-format
+msgid "unable to write repeatedly vanishing file %s"
+msgstr "impossible d'écrire le fichier %s qui disparaît répétitivement"
+
+#, c-format
msgid "unable to set permission to '%s'"
msgstr "impossible de régler les droits de '%s'"
@@ -19338,6 +19386,52 @@ msgstr "bascule inconnue « %c »"
msgid "unknown non-ascii option in string: `%s'"
msgstr "option non-ascii inconnue dans la chaîne : '%s'"
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the long form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[=<%s>]"
+msgstr "[=<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the short form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[<%s>]"
+msgstr "[<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string stands for a
+#. value given to a command line option, and "<>" is there
+#. as a convention to signal that it is a placeholder
+#. (i.e. the user should substitute it with the real value).
+#. If your language uses a different convention, you can
+#. change "<%s>" part to match yours, e.g. it might use
+#. "|%s|" instead, or if the alphabet is different enough it
+#. may use "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid " <%s>"
+msgstr " <%s>"
+
msgid "..."
msgstr "..."
@@ -19425,6 +19519,21 @@ msgid "failed to parse %s"
msgstr "échec de l'analyse de %s"
#, c-format
+msgid "failed to walk children of tree %s: not found"
+msgstr "échec de parcours des enfants de l'arbre %s : non trouvé"
+
+#, c-format
+msgid "failed to find object %s"
+msgstr "échec de la recherche de l'objet %s"
+
+#, c-format
+msgid "failed to find tag %s"
+msgstr "impossible de trouver l'étiquette %s"
+
+msgid "failed to setup revision walk"
+msgstr "impossible définir un parcours de révisions"
+
+#, c-format
msgid "Could not make %s writable by group"
msgstr "Impossible de rendre %s inscriptible pour le groupe"
@@ -19581,6 +19690,22 @@ msgstr "un nom de prometteur distant ne peut pas commencer par '/' : %s"
msgid "could not fetch %s from promisor remote"
msgstr "impossible de récupérer %s depuis le distant de prometteur"
+#, c-format
+msgid "known remote named '%s' but with url '%s' instead of '%s'"
+msgstr "distant connu nommé '%s' mais avec l'url '%s' au lieu de '%s'"
+
+#, c-format
+msgid "unknown '%s' value for '%s' config option"
+msgstr "valeur inconnue '%s' pour l'option de config '%s'"
+
+#, c-format
+msgid "unknown element '%s' from remote info"
+msgstr "élément inconnu '%s' pour l'info de distant"
+
+#, c-format
+msgid "accepted promisor remote '%s' not found"
+msgstr "distant accpté de prometteur '%s' non trouvé"
+
msgid "object-info: expected flush after arguments"
msgstr "object-info : vidage attendu après les arguments"
@@ -20418,6 +20543,14 @@ msgid "invalid refspec '%s'"
msgstr "spécificateur de réference invalide : '%s'"
#, c-format
+msgid "pattern '%s' has no '*'"
+msgstr "la valeur '%s' du motif n'a pas de '*'"
+
+#, c-format
+msgid "replacement '%s' has no '*'"
+msgstr "le remplacement '%s' n'a pas de '*'"
+
+#, c-format
msgid "invalid quoting in push-option value: '%s'"
msgstr "citation invalide dans la valeur push-option : '%s'"
@@ -20539,6 +20672,28 @@ msgid "remote-curl: unknown command '%s' from git"
msgstr "remote-curl : commande inconnue '%s' depuis git"
#, c-format
+msgid ""
+"reading remote from \"%s/%s\", which is nominated for removal.\n"
+"\n"
+"If you still use the \"remotes/\" directory it is recommended to\n"
+"migrate to config-based remotes:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"If you cannot, please let us know why you still need to use it by\n"
+"sending an e-mail to <git@vger.kernel.org>."
+msgstr ""
+"lecture depuis \"%s/%s\", dont la suppression est programmée.\n"
+"\n"
+"Si vous utilisez encore le répertoire \"remotes\", il est recommandé de\n"
+"migrer vers une gestion à base de configuration :\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"S'il vous est impossible de migrer, veuillez nous avertir par\n"
+"un courriel à <git@vger.kernel.org>."
+
+#, c-format
msgid "config remote shorthand cannot begin with '/': %s"
msgstr ""
"un raccourci de configuration de distant ne peut pas commencer par '/' : %s"
@@ -20574,14 +20729,6 @@ msgid "%s tracks both %s and %s"
msgstr "%s suit à la fois %s et %s"
#, c-format
-msgid "key '%s' of pattern had no '*'"
-msgstr "la clé '%s' du modèle n'a pas de '*'"
-
-#, c-format
-msgid "value '%s' of pattern has no '*'"
-msgstr "la valeur '%s' du modèle n'a pas de '*'"
-
-#, c-format
msgid "src refspec %s does not match any"
msgstr ""
"le spécificateur de référence source %s ne correspond à aucune référence"
@@ -22505,6 +22652,27 @@ 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 "test-tool path-walk <options> -- <revision-options>"
+msgstr "test-tool path-walk <options> -- <options-de-révision>"
+
+msgid "toggle inclusion of blob objects"
+msgstr "activer l'inclusion des objets blob"
+
+msgid "toggle inclusion of commit objects"
+msgstr "activer l'inclusion des objets commit"
+
+msgid "toggle inclusion of tag objects"
+msgstr "activer l'inclusion des objets étiquette"
+
+msgid "toggle inclusion of tree objects"
+msgstr "activer l'inclusion des objets arbre"
+
+msgid "toggle pruning of uninteresting paths"
+msgstr "activer l'élagage des chemins inintéressants"
+
+msgid "read a pattern list over stdin"
+msgstr "lire les motifs depuis stdin"
+
#, c-format
msgid "commit %s is not marked reachable"
msgstr "le commit %s n'est pas marqué joignable"
@@ -23151,6 +23319,10 @@ msgstr "erreur : "
msgid "warning: "
msgstr "avertissement : "
+#, c-format
+msgid "uname() failed with error '%s' (%d)\n"
+msgstr "échec de uname() avec l'erreur '%s' (%d)\n"
+
msgid "Fetching objects"
msgstr "Récupération des objets"
@@ -24128,5 +24300,23 @@ msgid "Do you really want to send %s? [y|N]: "
msgstr "Souhaitez-vous réellement envoyer %s ?[y|N] : "
#, c-format
+#~ msgid "Could not find remote branch %s to clone."
+#~ msgstr "Impossible de trouver la branche distante '%s' à cloner."
+
+#, c-format
+#~ msgid "merging cannot continue; got unclean result of %d"
+#~ msgstr "la fusion ne peut pas continuer ; résultat non propre retourné %d"
+
+#~ msgid "git repack [<options>]"
+#~ msgstr "git repack [<options>]"
+
+#~ msgid "--onto and --advance are incompatible"
+#~ msgstr "--onto et --advance sont incompatibles"
+
+#, c-format
+#~ msgid "key '%s' of pattern had no '*'"
+#~ msgstr "la clé '%s' du modèle n'a pas de '*'"
+
+#, c-format
#~ msgid "preferred pack (%s) is invalid"
#~ msgstr "le paquet préféré (%s) est invalide"
diff --git a/po/id.po b/po/id.po
index 3965c9e941..7d32c51ac7 100644
--- 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: 2024-12-23 18:57+0000\n"
-"PO-Revision-Date: 2025-01-06 15:50+0700\n"
+"POT-Creation-Date: 2025-03-05 22:57+0000\n"
+"PO-Revision-Date: 2025-03-09 17:44+0700\n"
"Last-Translator: Bagas Sanjaya <bagasdotme@gmail.com>\n"
"Language-Team: Indonesian\n"
"Language: id\n"
@@ -2890,6 +2890,22 @@ msgstr "git archive: kesalahan protokol"
msgid "git archive: expected a flush"
msgstr "git archive: sebuah bilasan diharapkan"
+#: builtin/backfill.c
+msgid "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+msgstr "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+
+#: builtin/backfill.c
+msgid "problem loading sparse-checkout"
+msgstr "galat memuat sparse-checkout"
+
+#: builtin/backfill.c
+msgid "Minimum number of objects to request at a time"
+msgstr "Jumlah objek minum yang diminta pada suatu waktu"
+
+#: builtin/backfill.c
+msgid "Restrict the missing objects to the current sparse-checkout"
+msgstr "Batasi objek yang hilang ke sparse-checkout saat ini"
+
#: builtin/bisect.c
msgid ""
"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>] [--no-"
@@ -3312,7 +3328,7 @@ msgstr "perlihatkan email pengarang daripada nama (asali: off)"
msgid "ignore whitespace differences"
msgstr "abaikan perbedaan spasi putih"
-#: builtin/blame.c builtin/log.c
+#: builtin/blame.c builtin/clone.c builtin/log.c
msgid "rev"
msgstr "revisi"
@@ -3829,11 +3845,6 @@ msgid "git version:\n"
msgstr "versi git:\n"
#: builtin/bugreport.c
-#, c-format
-msgid "uname() failed with error '%s' (%d)\n"
-msgstr "uname() gagal dengan kesalahan '%s' (%d)\n"
-
-#: builtin/bugreport.c
msgid "compiler info: "
msgstr "info pengompilasi: "
@@ -5090,8 +5101,112 @@ msgid "clean.requireForce is true and -f not given: refusing to clean"
msgstr "clean.requireForce 'true' dan -f tidak diberikan: menolak membersihkan"
#: builtin/clone.c
-msgid "git clone [<options>] [--] <repo> [<dir>]"
-msgstr "git clone [<opsi>] [--] <repo> [<direktori>]"
+#, c-format
+msgid "info: Could not add alternate for '%s': %s\n"
+msgstr "info: Tidak dapat menambahkan alternatif untuk '%s': %s\n"
+
+#: builtin/clone.c builtin/diff.c builtin/rm.c grep.c setup.c
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "gagal men-stat '%s'"
+
+#: builtin/clone.c
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s ada dan bukan direktori"
+
+#: builtin/clone.c
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "'%s' tautan simbolik, menolak mengkloning dengan --local"
+
+#: builtin/clone.c
+#, c-format
+msgid "failed to start iterator over '%s'"
+msgstr "gagal memulai iterator pada '%s'"
+
+#: builtin/clone.c
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr "tautan simbolik '%s' ada, menolak mengkloning dengan --local"
+
+#: builtin/clone.c compat/precompose_utf8.c
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "gagal menghapus tautan '%s'"
+
+#: builtin/clone.c
+#, c-format
+msgid "hardlink cannot be checked at '%s'"
+msgstr "tautan keras tidak dapat diperiksa pada '%s'"
+
+#: builtin/clone.c
+#, c-format
+msgid "hardlink different from source at '%s'"
+msgstr "tautan keras berbeda dari sumber pada '%s'"
+
+#: builtin/clone.c
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "gagal membuat tautan '%s'"
+
+#: builtin/clone.c
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "gagal menyalin berkas ke '%s'"
+
+#: builtin/clone.c refs/files-backend.c
+#, c-format
+msgid "failed to iterate over '%s'"
+msgstr "gagal iterasi pada '%s'"
+
+#: builtin/clone.c
+#, c-format
+msgid "done.\n"
+msgstr "selesai.\n"
+
+#: builtin/clone.c
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry with 'git restore --source=HEAD :/'\n"
+msgstr ""
+"Klon sukses, tapi checkout gagal.\n"
+"Anda dapat periksa apa yang dicheckout dengan 'git status'\n"
+"dan coba lagi dengan 'git restore --source=HEAD :/'\n"
+
+#: builtin/clone.c fetch-pack.c
+msgid "remote did not send all necessary objects"
+msgstr "remote tidak mengirim semua objek yang dibutuhkan"
+
+#: builtin/clone.c
+#, c-format
+msgid "unable to update %s"
+msgstr "tidak dapat memperbarui %s"
+
+#: builtin/clone.c
+msgid "failed to initialize sparse-checkout"
+msgstr "gagal menginisalisasi checkout tipis"
+
+#: builtin/clone.c
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
+msgstr "HEAD remote merujuk pada ref yang tidak ada, tidak dapat men-checkout"
+
+#: builtin/clone.c
+msgid "unable to checkout working tree"
+msgstr "tidak dapat men-checkout pohon kerja"
+
+#: builtin/clone.c
+msgid "unable to write parameters to config file"
+msgstr "tidak dapat menulis parameter ke berkas konfigurasi"
+
+#: builtin/clone.c
+msgid "cannot repack to clean up"
+msgstr "tidak dapat memaket ulang untuk pembersihan"
+
+#: builtin/clone.c
+msgid "cannot unlink temporary alternates file"
+msgstr "tidak dapat batal-taut berkas alternatif sementara"
#: builtin/clone.c
msgid "don't clone shallow repository"
@@ -5164,6 +5279,10 @@ msgid "checkout <branch> instead of the remote's HEAD"
msgstr "checkout <cabang> daripada HEAD remote"
#: builtin/clone.c
+msgid "clone single revision <rev> and check out"
+msgstr "klon satu revisi <revisi> dan check out"
+
+#: builtin/clone.c
msgid "path to git-upload-pack on the remote"
msgstr "jalur ke git-upload-pack pada remote"
@@ -5192,8 +5311,8 @@ msgid "clone only one branch, HEAD or --branch"
msgstr "klon hanya satu cabang, HEAD atau --branch"
#: builtin/clone.c
-msgid "don't clone any tags, and make later fetches not to follow them"
-msgstr "jangan klon tag apapun, dan buat pengambilan nanti tidak mengikutinya"
+msgid "clone tags, and make later fetches not to follow them"
+msgstr "klon tag dan jangan ikuti mereka pada pengambilan berikutnya"
#: builtin/clone.c
msgid "any cloned submodules will be shallow"
@@ -5251,117 +5370,8 @@ msgid "a URI for downloading bundles before fetching from origin remote"
msgstr "sebuah URI untuk mengunduh bundel sebelum mengambil dari remote asal"
#: builtin/clone.c
-#, c-format
-msgid "info: Could not add alternate for '%s': %s\n"
-msgstr "info: Tidak dapat menambahkan alternatif untuk '%s': %s\n"
-
-#: builtin/clone.c builtin/diff.c builtin/rm.c grep.c setup.c
-#, c-format
-msgid "failed to stat '%s'"
-msgstr "gagal men-stat '%s'"
-
-#: builtin/clone.c
-#, c-format
-msgid "%s exists and is not a directory"
-msgstr "%s ada dan bukan direktori"
-
-#: builtin/clone.c
-#, c-format
-msgid "'%s' is a symlink, refusing to clone with --local"
-msgstr "'%s' tautan simbolik, menolak mengkloning dengan --local"
-
-#: builtin/clone.c
-#, c-format
-msgid "failed to start iterator over '%s'"
-msgstr "gagal memulai iterator pada '%s'"
-
-#: builtin/clone.c
-#, c-format
-msgid "symlink '%s' exists, refusing to clone with --local"
-msgstr "tautan simbolik '%s' ada, menolak mengkloning dengan --local"
-
-#: builtin/clone.c compat/precompose_utf8.c
-#, c-format
-msgid "failed to unlink '%s'"
-msgstr "gagal menghapus tautan '%s'"
-
-#: builtin/clone.c
-#, c-format
-msgid "hardlink cannot be checked at '%s'"
-msgstr "tautan keras tidak dapat diperiksa pada '%s'"
-
-#: builtin/clone.c
-#, c-format
-msgid "hardlink different from source at '%s'"
-msgstr "tautan keras berbeda dari sumber pada '%s'"
-
-#: builtin/clone.c
-#, c-format
-msgid "failed to create link '%s'"
-msgstr "gagal membuat tautan '%s'"
-
-#: builtin/clone.c
-#, c-format
-msgid "failed to copy file to '%s'"
-msgstr "gagal menyalin berkas ke '%s'"
-
-#: builtin/clone.c refs/files-backend.c
-#, c-format
-msgid "failed to iterate over '%s'"
-msgstr "gagal iterasi pada '%s'"
-
-#: builtin/clone.c
-#, c-format
-msgid "done.\n"
-msgstr "selesai.\n"
-
-#: builtin/clone.c
-msgid ""
-"Clone succeeded, but checkout failed.\n"
-"You can inspect what was checked out with 'git status'\n"
-"and retry with 'git restore --source=HEAD :/'\n"
-msgstr ""
-"Klon sukses, tapi checkout gagal.\n"
-"Anda dapat periksa apa yang dicheckout dengan 'git status'\n"
-"dan coba lagi dengan 'git restore --source=HEAD :/'\n"
-
-#: builtin/clone.c
-#, c-format
-msgid "Could not find remote branch %s to clone."
-msgstr "Tidak dapat menemukan cabang remote %s untuk diklon."
-
-#: builtin/clone.c fetch-pack.c
-msgid "remote did not send all necessary objects"
-msgstr "remote tidak mengirim semua objek yang dibutuhkan"
-
-#: builtin/clone.c
-#, c-format
-msgid "unable to update %s"
-msgstr "tidak dapat memperbarui %s"
-
-#: builtin/clone.c
-msgid "failed to initialize sparse-checkout"
-msgstr "gagal menginisalisasi checkout tipis"
-
-#: builtin/clone.c
-msgid "remote HEAD refers to nonexistent ref, unable to checkout"
-msgstr "HEAD remote merujuk pada ref yang tidak ada, tidak dapat men-checkout"
-
-#: builtin/clone.c
-msgid "unable to checkout working tree"
-msgstr "tidak dapat men-checkout pohon kerja"
-
-#: builtin/clone.c
-msgid "unable to write parameters to config file"
-msgstr "tidak dapat menulis parameter ke berkas konfigurasi"
-
-#: builtin/clone.c
-msgid "cannot repack to clean up"
-msgstr "tidak dapat memaket ulang untuk pembersihan"
-
-#: builtin/clone.c
-msgid "cannot unlink temporary alternates file"
-msgstr "tidak dapat batal-taut berkas alternatif sementara"
+msgid "git clone [<options>] [--] <repo> [<dir>]"
+msgstr "git clone [<opsi>] [--] <repo> [<direktori>]"
#: builtin/clone.c
msgid "Too many arguments."
@@ -5492,6 +5502,11 @@ msgid "Remote branch %s not found in upstream %s"
msgstr "Cabang remote %s tidak ditemukan di hulu %s"
#: builtin/clone.c
+#, c-format
+msgid "Remote revision %s not found in upstream %s"
+msgstr "Revisi remote %s tidak ditemukan di hulu %s"
+
+#: builtin/clone.c
msgid "You appear to have cloned an empty repository."
msgstr "Anda tampaknya mengklon repositori kosong."
@@ -5555,7 +5570,8 @@ msgstr ""
"[no-]progress]\n"
" <opsi pemisahan>"
-#: builtin/commit-graph.c builtin/fetch.c builtin/log.c builtin/repack.c
+#: builtin/commit-graph.c builtin/fetch.c builtin/gc.c builtin/log.c
+#: builtin/repack.c
msgid "dir"
msgstr "direktori"
@@ -5715,7 +5731,7 @@ msgstr "git commit-tree: gagal membaca"
#: builtin/commit.c
msgid ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
"reword):]<commit>]\n"
" [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
@@ -5725,7 +5741,7 @@ msgid ""
" [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
" [--] [<pathspec>...]"
msgstr ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <komit> | --fixup [(amend|"
"reword):]<komit>]\n"
" [-F <berkas> | -m <pesan>] [--reset-author] [--allow-empty]\n"
@@ -7472,14 +7488,14 @@ msgid ""
"Run 'git remote set-head %s %s' to follow the change, or set\n"
"'remote.%s.followRemoteHEAD' configuration option to a different value\n"
"if you do not want to see this message. Specifically running\n"
-"'git config set remote.%s.followRemoteHEAD %s' will disable the warning\n"
-"until the remote changes HEAD to something else."
+"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n"
+"will disable the warning until the remote changes HEAD to something else."
msgstr ""
"Jalankan 'git remote set-head %s %s' untuk mengikuti perubahan, atau setel\n"
"opsi konfigurasi 'remote.%s.followRemoteHEAD' ke nilai yang berbeda jika\n"
-"Anda tidak ingin melihat pesan ini lagi. Secara rinci menjalakan\n"
-"'git config set remote.%s followRemoteHEAD %s' akan mematikan peringatan\n"
-"ini sampai remote mengubah HEAD ke yang lain."
+"Anda tidak ingin melihat pesan ini lagi. Secara rinci menjalankan\n"
+"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s' akan\n"
+"mematikan peringatan ini sampai remote mengubah HEAD ke yang lain."
#: builtin/fetch.c
msgid "multiple branches detected, incompatible with --set-upstream"
@@ -8341,6 +8357,10 @@ msgstr "paksa jalankan gc bahkan jika mungkin ada gc lain yang berjalan"
msgid "repack all other packs except the largest pack"
msgstr "pak ulang semua pak yang lain kecuali pak terbesar"
+#: builtin/gc.c builtin/repack.c
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr "awalan pak untuk menyimpan pak berisi objek terpangkas"
+
#: builtin/gc.c
#, c-format
msgid "failed to parse gc.logExpiry value %s"
@@ -9353,6 +9373,11 @@ msgstr "tidak dapat menyelesaikan pack-objects untuk mempak ulang tautan lokal"
msgid "Cannot come back to cwd"
msgstr "tidak dapat kembali ke direktori kerja saat ini"
+#: builtin/index-pack.c builtin/unpack-objects.c
+#, c-format
+msgid "bad --pack_header: %s"
+msgstr "--pack_header jelek: %s"
+
#: builtin/index-pack.c
#, c-format
msgid "bad %s"
@@ -10476,11 +10501,6 @@ msgstr "opsi strategi tidak dikenal: -X%s"
msgid "malformed input line: '%s'."
msgstr "baris masukan jelek: '%s'."
-#: builtin/merge-tree.c
-#, c-format
-msgid "merging cannot continue; got unclean result of %d"
-msgstr "penggabungan tidak dapat berlanjut; dapat hasil kotor dari %d"
-
#: builtin/merge.c
msgid "git merge [<options>] [<commit>...]"
msgstr "git merge [<opsi>] [<komit>...]"
@@ -11524,6 +11544,15 @@ msgstr ""
#: builtin/pack-objects.c
#, c-format
+msgid "invalid --name-hash-version option: %d"
+msgstr "opsi --name-hash-version tidak valid: %d"
+
+#: builtin/pack-objects.c
+msgid "currently, --write-bitmap-index requires --name-hash-version=1"
+msgstr "saat ini, --write-bitmap-index memerlukan --name-hash-version=1"
+
+#: builtin/pack-objects.c
+#, c-format
msgid ""
"write_reuse_object: could not locate %s, expected at offset %<PRIuMAX> in "
"pack %s"
@@ -11935,6 +11964,12 @@ msgstr ""
"protokol ini"
#: builtin/pack-objects.c
+msgid "use the specified name-hash function to group similar objects"
+msgstr ""
+"gunakan fungsi nama-hash yang dirincikan untuk mengelompokan objek yang "
+"serupa"
+
+#: builtin/pack-objects.c
#, c-format
msgid "delta chain depth %d is too deep, forcing %d"
msgstr "kedalaman rantai delta %d terlalu dalam, memaksakan %d"
@@ -13428,8 +13463,8 @@ msgid "invalid ref format: %s"
msgstr "format referensi tidak valid: %s"
#: builtin/refs.c
-msgid "git refs migrate --ref-format=<format> [--dry-run]"
-msgstr "git refs migrate --ref-format=<format> [--dry-run]"
+msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
+msgstr "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
#: builtin/refs.c
msgid "git refs verify [--strict] [--verbose]"
@@ -13444,6 +13479,10 @@ msgid "perform a non-destructive dry-run"
msgstr "lakukan uji coba non desktruktif"
#: builtin/refs.c
+msgid "drop reflogs entirely during the migration"
+msgstr "buang keseluruhan log referensi selama migrasi"
+
+#: builtin/refs.c
msgid "missing --ref-format=<format>"
msgstr "--ref-format=<format> hilang"
@@ -14024,8 +14063,14 @@ msgid "be verbose; must be placed before a subcommand"
msgstr "jadi lebih bertele-tele; harus ditempatkan sebelum subperintah"
#: builtin/repack.c
-msgid "git repack [<options>]"
-msgstr "git repack [<opsi>]"
+msgid ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
+msgstr ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<nama pak>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
#: builtin/repack.c
msgid ""
@@ -14119,6 +14164,13 @@ msgid "pass --no-reuse-object to git-pack-objects"
msgstr "lewatkan --no-reuse-object ke git-pack-objects"
#: builtin/repack.c
+msgid ""
+"specify the name hash version to use for grouping similar objects by path"
+msgstr ""
+"tentukan versi nama hash yang digunakan untuk mengelompokan objek yang "
+"serupa berdasarkan jalur"
+
+#: builtin/repack.c
msgid "do not run git-update-server-info"
msgstr "jangan jalankan git-update-server-info"
@@ -14185,10 +14237,6 @@ msgid "write a multi-pack index of the resulting packs"
msgstr "tulis indeks multipak dari pak yang dihasilkan"
#: builtin/repack.c
-msgid "pack prefix to store a pack containing pruned objects"
-msgstr "awalan pak untuk menyimpan pak berisi objek terpangkas"
-
-#: builtin/repack.c
msgid "pack prefix to store a pack containing filtered out objects"
msgstr "awalan pak untuk menyimpan pak berisi objek tersaring"
@@ -14463,10 +14511,6 @@ 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"
@@ -17768,6 +17812,10 @@ msgid "Create an archive of files from a named tree"
msgstr "Buat arsip berkas dari pohon bernama"
#: command-list.h
+msgid "Download missing objects in a partial clone"
+msgstr "Unduh objek yang hilang dalam klon parsial"
+
+#: command-list.h
msgid "Use binary search to find the commit that introduced a bug"
msgstr "Gunakan pencarian biner untuk mencari komit yang memasukkan bug"
@@ -20191,6 +20239,14 @@ msgid "invalid regex given to -I: '%s'"
msgstr "regex tidak valid diberikan ke -I: '%s'"
#: diff.c
+msgid "-G requires a non-empty argument"
+msgstr "-G butuh sebuah argumen bukan kosong"
+
+#: diff.c
+msgid "-S requires a non-empty argument"
+msgstr "-S butuh sebuah argumen bukan kosong"
+
+#: diff.c
#, c-format
msgid "failed to parse --submodule option parameter: '%s'"
msgstr "gagal menguraikan parameter opsi --submodule: '%s'"
@@ -22888,6 +22944,11 @@ msgstr "tidak dapat menulis berkas %s"
#: object-file.c
#, c-format
+msgid "unable to write repeatedly vanishing file %s"
+msgstr "tidak dapat menulis berkas yang menghilang %s terus-menerus"
+
+#: object-file.c
+#, c-format
msgid "unable to set permission to '%s'"
msgstr "tidak dapat menyetel perizinan ke '%s'"
@@ -23579,6 +23640,55 @@ msgstr "sakelar tidak dikenal `%c'"
msgid "unknown non-ascii option in string: `%s'"
msgstr "opsi non-ascii di dalam untai tidak dikenal: `%s'"
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the long form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#: parse-options.c
+#, c-format
+msgid "[=<%s>]"
+msgstr "[=<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the short form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#: parse-options.c
+#, c-format
+msgid "[<%s>]"
+msgstr "[<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string stands for a
+#. value given to a command line option, and "<>" is there
+#. as a convention to signal that it is a placeholder
+#. (i.e. the user should substitute it with the real value).
+#. If your language uses a different convention, you can
+#. change "<%s>" part to match yours, e.g. it might use
+#. "|%s|" instead, or if the alphabet is different enough it
+#. may use "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#: parse-options.c
+#, c-format
+msgid " <%s>"
+msgstr " <%s>"
+
#: parse-options.c
msgid "..."
msgstr "..."
@@ -23682,6 +23792,25 @@ msgstr "nilai lingkungan boolean '%s' jelek untuk '%s'"
msgid "failed to parse %s"
msgstr "gagal menguraikan %s"
+#: path-walk.c
+#, c-format
+msgid "failed to walk children of tree %s: not found"
+msgstr "gagal berjalan anak pohon %s: tidak ditemukan"
+
+#: path-walk.c
+#, c-format
+msgid "failed to find object %s"
+msgstr "gagal menemukan objek %s"
+
+#: path-walk.c
+#, c-format
+msgid "failed to find tag %s"
+msgstr "gagal menemukan tag %s"
+
+#: path-walk.c
+msgid "failed to setup revision walk"
+msgstr "gagal men-setup jalan revisi"
+
#: path.c
#, c-format
msgid "Could not make %s writable by group"
@@ -23868,6 +23997,26 @@ msgstr "nama remote penjanji tidak dapat diawali dengan '/': %s"
msgid "could not fetch %s from promisor remote"
msgstr "tidak dapat mengambil %s dari remote penjanji"
+#: promisor-remote.c
+#, c-format
+msgid "known remote named '%s' but with url '%s' instead of '%s'"
+msgstr "remote yang terkenal bernama '%s' tapi dengan url '%s' daripada '%s'"
+
+#: promisor-remote.c
+#, c-format
+msgid "unknown '%s' value for '%s' config option"
+msgstr "nilai '%s' tidak dikenal untuk opsi konfigurasi '%s'"
+
+#: promisor-remote.c
+#, c-format
+msgid "unknown element '%s' from remote info"
+msgstr "elemen '%s' dari info remote tidak dikenal"
+
+#: promisor-remote.c
+#, c-format
+msgid "accepted promisor remote '%s' not found"
+msgstr "remote penjanji yang diterima '%s' tidak ditemukan"
+
#: protocol-caps.c
msgid "object-info: expected flush after arguments"
msgstr "object-info: bilasan diharapkan setelah argumen"
@@ -24870,6 +25019,16 @@ msgstr "nama referensi %s simbolik, menyalinnya tidak didukung"
msgid "invalid refspec '%s'"
msgstr "spek referensi tidak valid '%s'"
+#: refspec.c
+#, c-format
+msgid "pattern '%s' has no '*'"
+msgstr "pola '%s' tidak mempunyai '*'"
+
+#: refspec.c
+#, c-format
+msgid "replacement '%s' has no '*'"
+msgstr "pengganti '%s' tidak mempunyai '*'"
+
#: remote-curl.c
#, c-format
msgid "invalid quoting in push-option value: '%s'"
@@ -25025,6 +25184,28 @@ msgstr "remote-curl: perintah tidak dikenal '%s' dari git"
#: remote.c
#, c-format
+msgid ""
+"reading remote from \"%s/%s\", which is nominated for removal.\n"
+"\n"
+"If you still use the \"remotes/\" directory it is recommended to\n"
+"migrate to config-based remotes:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"If you cannot, please let us know why you still need to use it by\n"
+"sending an e-mail to <git@vger.kernel.org>."
+msgstr ""
+"membaca remote dari \"%s/%s\", yang dinominasikan untuk dihapus.\n"
+"\n"
+"Jika Anda masih menggunakan direktori \"remotes/\" disarankan untuk\n"
+"migrasi ke remote berdasarkan konfigurasi:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"Jika tidak, mohon beri tahu kami mengapa Anda masih menggunakannya dengan\n"
+"mengirimkan surel ke <git@vger.kernel.org>."
+
+#: remote.c
+#, c-format
msgid "config remote shorthand cannot begin with '/': %s"
msgstr "pintasan konfigurasi remote tidak dapat diawali dengan '/': %s"
@@ -25068,16 +25249,6 @@ msgstr "%s melacak baik %s dan %s"
#: remote.c
#, c-format
-msgid "key '%s' of pattern had no '*'"
-msgstr "kunci '%s' dari pola tidak ada '*'"
-
-#: remote.c
-#, c-format
-msgid "value '%s' of pattern has no '*'"
-msgstr "nilai '%s' dari pola tidak ada '*'"
-
-#: remote.c
-#, c-format
msgid "src refspec %s does not match any"
msgstr "spek referensi sumber %s tidak cocok dengan apapun"
@@ -27389,6 +27560,34 @@ 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-path-walk.c
+msgid "test-tool path-walk <options> -- <revision-options>"
+msgstr "test-tool path-walk <opsi> -- <opsi revisi>"
+
+#: t/helper/test-path-walk.c
+msgid "toggle inclusion of blob objects"
+msgstr "sertakan objek blob"
+
+#: t/helper/test-path-walk.c
+msgid "toggle inclusion of commit objects"
+msgstr "sertakan objek komit"
+
+#: t/helper/test-path-walk.c
+msgid "toggle inclusion of tag objects"
+msgstr "sertakan objek tag"
+
+#: t/helper/test-path-walk.c
+msgid "toggle inclusion of tree objects"
+msgstr "sertakan objek pohon"
+
+#: t/helper/test-path-walk.c
+msgid "toggle pruning of uninteresting paths"
+msgstr "pangkas jalur yang tidak menarik"
+
+#: t/helper/test-path-walk.c
+msgid "read a pattern list over stdin"
+msgstr "baca daftar pola dari masukan standar"
+
#: t/helper/test-reach.c
#, c-format
msgid "commit %s is not marked reachable"
@@ -28148,6 +28347,11 @@ msgstr "kesalahan: "
msgid "warning: "
msgstr "peringatan: "
+#: version.c
+#, c-format
+msgid "uname() failed with error '%s' (%d)\n"
+msgstr "uname() gagal dengan kesalahan '%s' (%d)\n"
+
#: walker.c
msgid "Fetching objects"
msgstr "Mengambil objek"
diff --git a/po/it.po b/po/it.po
index c31560834d..20fd8bb28d 100644
--- a/po/it.po
+++ b/po/it.po
@@ -17602,7 +17602,7 @@ msgstr "Controllo la ridenominazione di '%s' in '%s'\n"
#: builtin/mv.c:185
msgid "bad source"
-msgstr "sourgente errata"
+msgstr "sorgente errata"
#: builtin/mv.c:188
msgid "can not move directory into itself"
diff --git a/po/ko.po b/po/ko.po
index 5d190e5238..7a6847f023 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -7,6 +7,7 @@
# Changwoo Ryu <cwryu@debian.org>, 2015-2018.
# Sihyeon Jang <uneedsihyeon@gmail.com>, 2018.
# Gwan-gyeong Mun <elongbug@gmail.com>, 2018.
+# Seoyeon Kwon <syeon0204@gmail.com>, 2025.
#
# - 작업자는 위 Contributors 목록에 추가해 주세요.
# - 번역하면서 80컬럼을 넘어가지 않도록 해 주세요.
@@ -7666,7 +7667,7 @@ msgid ""
" git commit --allow-empty\n"
"\n"
msgstr ""
-"이전 커맷 빼오기가 비어 있습니다. 아마도 충돌 해결 과정에서 그렇게 됐을\n"
+"이전 커밋 빼오기가 비어 있습니다. 아마도 충돌 해결 과정에서 그렇게 됐을\n"
"것입니다. 그래도 커밋하려면 다음과 같이 하십시오:\n"
"\n"
" git commit --allow-empty\n"
diff --git a/po/sv.po b/po/sv.po
index 5377b3ece7..42c357e54f 100644
--- a/po/sv.po
+++ b/po/sv.po
@@ -1,14 +1,14 @@
# Swedish translations for Git.
-# Copyright (C) 2010-2024 Peter Krefting <peter@softwolves.pp.se>
+# Copyright (C) 2010-2025 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-2024.
+# Peter Krefting <peter@softwolves.pp.se>, 2010-2025.
#
msgid ""
msgstr ""
-"Project-Id-Version: git 2.48.0\n"
+"Project-Id-Version: git 2.49.0\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2024-12-30 11:57+0100\n"
-"PO-Revision-Date: 2024-12-30 12:03+0100\n"
+"POT-Creation-Date: 2025-03-10 17:45+0100\n"
+"PO-Revision-Date: 2025-03-10 17:48+0100\n"
"Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n"
"Language-Team: Svenska <tp-sv@listor.tp-sv.se>\n"
"Language: sv\n"
@@ -1477,7 +1477,7 @@ msgid ""
"Use '\\!' for literal leading exclamation."
msgstr ""
"Negativa mönster ignoreras i git-attribut\n"
-"Använd '\\!' för att inleda med ett utropstecken."
+"Använd ”\\!” för att inleda med ett utropstecken."
#, c-format
msgid "cannot fstat gitattributes file '%s'"
@@ -2060,7 +2060,7 @@ msgid ""
"It does not apply to blobs recorded in its index."
msgstr ""
"Har du handredigerat din patch?\n"
-"Den kan inte tillämpas på blobbar som antecknats i dess index."
+"Den kan inte tillämpas på blob:ar som antecknats i dess index."
msgid "Falling back to patching base and 3-way merge..."
msgstr ""
@@ -2316,6 +2316,18 @@ msgstr "git archive: protokollfel"
msgid "git archive: expected a flush"
msgstr "git archive: förväntade en tömning (flush)"
+msgid "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+msgstr "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+
+msgid "problem loading sparse-checkout"
+msgstr "problem med att läsa filen sparse-checkout"
+
+msgid "Minimum number of objects to request at a time"
+msgstr "Minsta antal objekt att be om varje gång"
+
+msgid "Restrict the missing objects to the current sparse-checkout"
+msgstr "Begränsa saknade objekt till befintlig ”sparse-checkout”"
+
msgid ""
"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>] [--no-"
"checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]"
@@ -2921,7 +2933,7 @@ msgid "delete branch (even if not merged)"
msgstr "ta bort gren (även om inte helt sammanslagen)"
msgid "move/rename a branch and its reflog"
-msgstr "flytta/ta bort en gren och dess reflogg"
+msgstr "flytta/ta bort en gren och dess referenslogg"
msgid "move/rename a branch, even if target exists"
msgstr "flytta/ta bort en gren, även om målet finns"
@@ -2930,7 +2942,7 @@ msgid "do not output a newline after empty formatted refs"
msgstr "skriv inte ut ett nyradstecken efter tomma formaterade referenser"
msgid "copy a branch and its reflog"
-msgstr "kopiera en gren och dess reflogg"
+msgstr "kopiera en gren och dess referenslogg"
msgid "copy a branch, even if target exists"
msgstr "kopiera en gren, även om målet finns"
@@ -2942,7 +2954,7 @@ msgid "show current branch name"
msgstr "visa namn på aktuell gren"
msgid "create the branch's reflog"
-msgstr "skapa grenens reflogg"
+msgstr "skapa grenens referenslogg"
msgid "edit the description for the branch"
msgstr "redigera beskrivning för grenen"
@@ -3055,10 +3067,6 @@ msgstr ""
msgid "git version:\n"
msgstr "git version:\n"
-#, c-format
-msgid "uname() failed with error '%s' (%d)\n"
-msgstr "uname() misslyckades med felet ”%s” (%d)\n"
-
msgid "compiler info: "
msgstr "kompilatorinfo:"
@@ -3095,7 +3103,7 @@ msgid ""
"You can delete any lines you don't wish to share.\n"
msgstr ""
"Tack för att du skriver en buggraport för Git!\n"
-"Om du svarar på följande frågor är det lättare för oss att första "
+"Om du svarar på följande frågor är det lättare för oss att förstå "
"problemet.\n"
"Skriv gärna på engelska\n"
"\n"
@@ -3332,11 +3340,11 @@ msgid "blob|tree"
msgstr "blob|träd"
msgid "use a <path> for (--textconv | --filters); Not with 'batch'"
-msgstr "använd en <sökväg> för (--textconv | --filters): Inte med 'batch'"
+msgstr "använd en <sökväg> för (--textconv | --filters): Inte med ”batch”"
#, c-format
msgid "'%s=<%s>' needs '%s' or '%s'"
-msgstr "'%s=<%s>' behöver '%s' eller '%s'"
+msgstr "”%s=<%s>” behöver ”%s” eller ”%s”"
msgid "path|tree-ish"
msgstr "sökväg|träd-igt"
@@ -3368,13 +3376,13 @@ msgid ""
"git check-attr [--source <tree-ish>] [-a | --all | <attr>...] [--] "
"<pathname>..."
msgstr ""
-"git check-attr [--source <träd:igt>] [-a | --all | <attr>...] [--] "
+"git check-attr [--source <träd-igt>] [-a | --all | <attr>...] [--] "
"<sökväg>..."
msgid ""
"git check-attr --stdin [-z] [--source <tree-ish>] [-a | --all | <attr>...]"
msgstr ""
-"git check-attr --stdin [-z] [--source <träd:igt>] [-a | --all | <attr>...]"
+"git check-attr --stdin [-z] [--source <träd-igt>] [-a | --all | <attr>...]"
msgid "report all attributes set on file"
msgstr "visa alla attribut som satts på filen"
@@ -3887,7 +3895,7 @@ msgid "create/reset and checkout a branch"
msgstr "skapa/nollställ och checka ut en gren"
msgid "create reflog for new branch"
-msgstr "skapa reflogg för ny gren"
+msgstr "skapa referenslogg för ny gren"
msgid "second guess 'git checkout <no-such-branch>' (default)"
msgstr "förutspå ”git checkout <gren-saknas>” (förval)"
@@ -4062,8 +4070,91 @@ msgstr "ta endast bort ignorerade filer"
msgid "clean.requireForce is true and -f not given: refusing to clean"
msgstr "clean.requireForce är true och -f angavs inte: vägrar städa"
-msgid "git clone [<options>] [--] <repo> [<dir>]"
-msgstr "git clone [<flaggor>] [--] <arkiv> [<kat>]"
+#, c-format
+msgid "info: Could not add alternate for '%s': %s\n"
+msgstr "info: Kan inte skapa suppleant för ”%s”: %s\n"
+
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "misslyckades ta status på ”%s”"
+
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s finns och är ingen katalog"
+
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "”%s” är en symbolisk länk, vägrar klona med --local"
+
+#, c-format
+msgid "failed to start iterator over '%s'"
+msgstr "misslyckades starta iterator över ”%s”"
+
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr "symbolisk länk ”%s” finns redan, vägrar klona med --local"
+
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "misslyckades ta bort länken ”%s”"
+
+#, c-format
+msgid "hardlink cannot be checked at '%s'"
+msgstr "hård länk kan inte kontrolleras vid ”%s”"
+
+#, c-format
+msgid "hardlink different from source at '%s'"
+msgstr "hård länk skiljer sig från källan vid ”%s”"
+
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "misslyckades skapa länken ”%s”"
+
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "misslyckades kopiera filen till ”%s”"
+
+#, c-format
+msgid "failed to iterate over '%s'"
+msgstr "misslyckades iterera över ”%s”"
+
+#, c-format
+msgid "done.\n"
+msgstr "klart.\n"
+
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry with 'git restore --source=HEAD :/'\n"
+msgstr ""
+"Klonen lyckades, men utcheckningen misslyckades.\n"
+"Du kan inspektera det som checkades ut med ”git status”\n"
+"och försöka med ”git restore --source=HEAD :/”\n"
+
+msgid "remote did not send all necessary objects"
+msgstr "fjärren sände inte alla nödvändiga objekt"
+
+#, c-format
+msgid "unable to update %s"
+msgstr "kan inte uppdatera %s"
+
+msgid "failed to initialize sparse-checkout"
+msgstr "misslyckades initiera sparse-checkout"
+
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
+msgstr "HEAD hos fjärren pekar på en obefintlig referens, kan inte checka ut"
+
+msgid "unable to checkout working tree"
+msgstr "kan inte checka ut arbetskatalogen"
+
+msgid "unable to write parameters to config file"
+msgstr "kan inte skriva parametrar till konfigurationsfilen"
+
+msgid "cannot repack to clean up"
+msgstr "kan inte packa om för att städa upp"
+
+msgid "cannot unlink temporary alternates file"
+msgstr "kunde inte ta bort temporär ”alternates”-fil"
msgid "don't clone shallow repository"
msgstr "klona inte grunt arkiv"
@@ -4116,6 +4207,9 @@ msgstr "använd <namn> istället för ”origin” för att spåra uppströms"
msgid "checkout <branch> instead of the remote's HEAD"
msgstr "checka ut <gren> istället för fjärrens HEAD"
+msgid "clone single revision <rev> and check out"
+msgstr "klona ensam revision <rev> och checka ut"
+
msgid "path to git-upload-pack on the remote"
msgstr "sökväg till git-upload-pack på fjärren"
@@ -4137,8 +4231,8 @@ msgstr "fördjupa historik för grund klon, exkludera ref"
msgid "clone only one branch, HEAD or --branch"
msgstr "klona endast en gren, HEAD eller --branch"
-msgid "don't clone any tags, and make later fetches not to follow them"
-msgstr "klona inga taggar och gör att senare hämtningar inte följer dem"
+msgid "clone tags, and make later fetches not to follow them"
+msgstr "klona taggar och gör att senare hämtningar inte följer dem"
msgid "any cloned submodules will be shallow"
msgstr "klonade undermoduler kommer vara grunda"
@@ -4179,95 +4273,8 @@ msgstr "uri"
msgid "a URI for downloading bundles before fetching from origin remote"
msgstr "en URI för att hämta buntar innan de hämtas från ursprungsfjärr"
-#, c-format
-msgid "info: Could not add alternate for '%s': %s\n"
-msgstr "info: Kan inte skapa suppleant för ”%s”: %s\n"
-
-#, c-format
-msgid "failed to stat '%s'"
-msgstr "misslyckades ta status på ”%s”"
-
-#, c-format
-msgid "%s exists and is not a directory"
-msgstr "%s finns och är ingen katalog"
-
-#, c-format
-msgid "'%s' is a symlink, refusing to clone with --local"
-msgstr "”%s” är en symbolisk länk, vägrar klona med --local"
-
-#, c-format
-msgid "failed to start iterator over '%s'"
-msgstr "misslyckades starta iterator över ”%s”"
-
-#, c-format
-msgid "symlink '%s' exists, refusing to clone with --local"
-msgstr "symbolisk länk ”%s” finns redan, vägrar klona med --local"
-
-#, c-format
-msgid "failed to unlink '%s'"
-msgstr "misslyckades ta bort länken ”%s”"
-
-#, c-format
-msgid "hardlink cannot be checked at '%s'"
-msgstr "hård länk kan inte kontrolleras vid ”%s”"
-
-#, c-format
-msgid "hardlink different from source at '%s'"
-msgstr "hård länk skiljer sig från källan vid ”%s”"
-
-#, c-format
-msgid "failed to create link '%s'"
-msgstr "misslyckades skapa länken ”%s”"
-
-#, c-format
-msgid "failed to copy file to '%s'"
-msgstr "misslyckades kopiera filen till ”%s”"
-
-#, c-format
-msgid "failed to iterate over '%s'"
-msgstr "misslyckades iterera över ”%s”"
-
-#, c-format
-msgid "done.\n"
-msgstr "klart.\n"
-
-msgid ""
-"Clone succeeded, but checkout failed.\n"
-"You can inspect what was checked out with 'git status'\n"
-"and retry with 'git restore --source=HEAD :/'\n"
-msgstr ""
-"Klonen lyckades, men utcheckningen misslyckades.\n"
-"Du kan inspektera det som checkades ut med ”git status”\n"
-"och försöka med ”git restore --source=HEAD :/”\n"
-
-#, c-format
-msgid "Could not find remote branch %s to clone."
-msgstr "Kunde inte hitta fjärrgrenen %s för att klona."
-
-msgid "remote did not send all necessary objects"
-msgstr "fjärren sände inte alla nödvändiga objekt"
-
-#, c-format
-msgid "unable to update %s"
-msgstr "kan inte uppdatera %s"
-
-msgid "failed to initialize sparse-checkout"
-msgstr "misslyckades initiera sparse-checkout"
-
-msgid "remote HEAD refers to nonexistent ref, unable to checkout"
-msgstr "HEAD hos fjärren pekar på en obefintlig referens, kan inte checka ut"
-
-msgid "unable to checkout working tree"
-msgstr "kan inte checka ut arbetskatalogen"
-
-msgid "unable to write parameters to config file"
-msgstr "kan inte skriva parametrar till konfigurationsfilen"
-
-msgid "cannot repack to clean up"
-msgstr "kan inte packa om för att städa upp"
-
-msgid "cannot unlink temporary alternates file"
-msgstr "kunde inte ta bort temporär ”alternates”-fil"
+msgid "git clone [<options>] [--] <repo> [<dir>]"
+msgstr "git clone [<flaggor>] [--] <arkiv> [<kat>]"
msgid "Too many arguments."
msgstr "För många argument."
@@ -4367,6 +4374,10 @@ msgstr "fjärrtransport rapporterade fel"
msgid "Remote branch %s not found in upstream %s"
msgstr "Fjärrgrenen %s hittades inte i uppströmsarkivet %s"
+#, c-format
+msgid "Remote revision %s not found in upstream %s"
+msgstr "Fjärr-revisionen %s hittades inte i uppströmsarkivet %s"
+
msgid "You appear to have cloned an empty repository."
msgstr "Du verkar ha klonat ett tomt arkiv."
@@ -4540,7 +4551,7 @@ msgid "git commit-tree: failed to read"
msgstr "git commit-tree: misslyckades läsa"
msgid ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
"reword):]<commit>]\n"
" [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
@@ -4550,7 +4561,7 @@ msgid ""
" [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
" [--] [<pathspec>...]"
msgstr ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<läge>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<läge>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <incheckning> | --fixup [(amend|"
"reword):]<incheckning>]\n"
" [-F <fil> | -m <medd>] [--reset-author] [--allow-empty]\n"
@@ -4791,7 +4802,7 @@ msgstr "Ange meddelandet en av flaggorna -m eller -F.\n"
#, c-format
msgid "--author '%s' is not 'Name <email>' and matches no existing author"
msgstr ""
-"--author '%s' är inte 'Namn <epost>' och motsvarar ingen befintlig författare"
+"--author ”%s” är inte ”Namn <epost>” och motsvarar ingen befintlig författare"
#, c-format
msgid "Invalid ignored mode '%s'"
@@ -5202,7 +5213,7 @@ msgid "writing to stdin is not supported"
msgstr "skriva till standard in stöds inte"
msgid "writing config blobs is not supported"
-msgstr "skriva konfigurations-blobbar stöds inte"
+msgstr "skriva konfigurations-blob:ar stöds inte"
#, c-format
msgid ""
@@ -5239,7 +5250,7 @@ msgid ""
"section in \"git help worktree\" for details"
msgstr ""
"--worktree kan inte användas med flera arbetskataloger om inte\n"
-"konfigurationsutöknignen worktreeConfig har aktiverats. Läsa stycket\n"
+"konfigurationsutökningen worktreeConfig har aktiverats. Läsa stycket\n"
"”KONFIGURATIONSFIL” i ”git help worktree” för detaljer"
msgid "Other"
@@ -5325,7 +5336,7 @@ msgid "editing stdin is not supported"
msgstr "redigering av standard in stöds ej"
msgid "editing blobs is not supported"
-msgstr "redigering av blobbar stöds ej"
+msgstr "redigering av blob:ar stöds ej"
#, c-format
msgid "cannot create configuration file %s"
@@ -5601,7 +5612,7 @@ msgstr "objektet ”%s” som angavs är felaktigt."
#, c-format
msgid "more than two blobs given: '%s'"
-msgstr "mer än två blobbar angavs: ”%s”"
+msgstr "mer än två blob:ar angavs: ”%s”"
#, c-format
msgid "unhandled object '%s' given."
@@ -5760,7 +5771,7 @@ msgid "reference parents which are not in fast-export stream by object id"
msgstr "referera föräldrar som inte finns i fast-export-ström med objekt-id"
msgid "show original object ids of blobs/commits"
-msgstr "visa ursprungliga objekt-id för blobbar/incheckningar"
+msgstr "visa ursprungliga objekt-id för blob:ar/incheckningar"
msgid "label tags with mark ids"
msgstr "märk taggar med märke-id"
@@ -5931,14 +5942,14 @@ msgid ""
"Run 'git remote set-head %s %s' to follow the change, or set\n"
"'remote.%s.followRemoteHEAD' configuration option to a different value\n"
"if you do not want to see this message. Specifically running\n"
-"'git config set remote.%s.followRemoteHEAD %s' will disable the warning\n"
-"until the remote changes HEAD to something else."
+"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n"
+"will disable the warning until the remote changes HEAD to something else."
msgstr ""
"Kör ”git remote set-head %s %s” för att följa ändringen, eller sätt\n"
"konfigurationsflaggan ”remote %s.followRemoteHEAD” till ett annat värde\n"
"om du inte vill se det här meddelandet. Du kan specifikt inaktivera\n"
"varningen till fjärren ändrar HEAD till något annat genom att köra\n"
-"”git config set remote %s.followRemoteHEAD %s”."
+"”git config set remote %s.followRemoteHEAD warn-if-not-branch-%s”."
msgid "multiple branches detected, incompatible with --set-upstream"
msgstr "flera grenar upptäcktes, inkompatibelt med --set-upstream"
@@ -6328,11 +6339,11 @@ msgstr "%s: objekt trasigt eller saknas"
#, c-format
msgid "%s: invalid reflog entry %s"
-msgstr "%s: ogiltig reflog-post %s"
+msgstr "%s: ogiltig referensloggpost %s"
#, c-format
msgid "Checking reflog %s->%s"
-msgstr "Kontrollerar reflog %s→%s"
+msgstr "Kontrollerar referenslogg %s→%s"
#, c-format
msgid "%s: invalid sha1 pointer %s"
@@ -6441,7 +6452,7 @@ msgid "make index objects head nodes"
msgstr "gör indexobjekt till huvudnoder"
msgid "make reflogs head nodes (default)"
-msgstr "gör refloggar till huvudnoder (standard)"
+msgstr "gör referensloggar till huvudnoder (standard)"
msgid "also consider packs and alternate objects"
msgstr "ta även hänsyn till paket och supplerande objekt"
@@ -6502,11 +6513,11 @@ msgstr "kunde inte skapa fsmonitor-kaka ”%s”"
#, c-format
msgid "fsmonitor: cookie_result '%d' != SEEN"
-msgstr "fsmonitor: cookie_result '%d' != SEEN"
+msgstr "fsmonitor: cookie_result ”%d” != SEEN"
#, c-format
msgid "could not start IPC thread pool on '%s'"
-msgstr "kunde inte starta IPC-trådpol på ”%s”"
+msgstr "kunde inte starta IPC-trådpool på ”%s”"
msgid "could not start fsmonitor listener thread"
msgstr "kunde inte starta fsmonitor-lyssnartråd"
@@ -6618,6 +6629,9 @@ msgstr "tvinga gc-körning även om en annan gc kanske körs"
msgid "repack all other packs except the largest pack"
msgstr "packa om alla paket förutom det största paketet"
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr "paketprefix att lagra ett paket som innehåller bortrensade objekt"
+
#, c-format
msgid "failed to parse gc.logExpiry value %s"
msgstr "misslyckades tolka värdet %s för gc.logExpiry"
@@ -7176,7 +7190,7 @@ msgstr "flaggan ”%s” tar inte några argument som inte är flaggor"
msgid ""
"the '--no-[external-commands|aliases]' options can only be used with '--all'"
msgstr ""
-"flaggorna '--no-[external-commands|aliases]' kan endast användas med ”--all”"
+"flaggorna ”--no-[external-commands|aliases]” kan endast användas med ”--all”"
#, c-format
msgid "usage: %s%s"
@@ -7407,6 +7421,10 @@ msgid "Cannot come back to cwd"
msgstr "Kan inte gå tillbaka till arbetskatalogen (cwd)"
#, c-format
+msgid "bad --pack_header: %s"
+msgstr "felaktig --pack_header: %s"
+
+#, c-format
msgid "bad %s"
msgstr "felaktig %s"
@@ -7977,7 +7995,7 @@ msgid "suppress duplicate entries"
msgstr "undertryck dublettposter"
msgid "show sparse directories in the presence of a sparse index"
-msgstr "visa glesa kataloger när et glest index existerar"
+msgstr "visa glesa kataloger när ett glest index existerar"
msgid ""
"--format cannot be used with -s, -o, -k, -t, --resolve-undo, --deduplicate, "
@@ -8261,10 +8279,6 @@ msgstr "okänd strategiflagga: -X%s"
msgid "malformed input line: '%s'."
msgstr "felaktig indatarad: ”%s”."
-#, c-format
-msgid "merging cannot continue; got unclean result of %d"
-msgstr "sammanslagning kan inte fortsätta; fick inte rent resultat från %d"
-
msgid "git merge [<options>] [<commit>...]"
msgstr "git merge [<flaggor>] [<incheckning>...]"
@@ -8969,7 +8983,7 @@ msgid "failed to remove 'git notes merge' worktree"
msgstr "misslyckades ta bort arbetskatalogen för ”git notes merge”"
msgid "failed to read ref NOTES_MERGE_PARTIAL"
-msgstr "misslyckades läsa references NOTES_MERGE_PARTIAL"
+msgstr "misslyckades läsa referensen NOTES_MERGE_PARTIAL"
msgid "could not find commit from NOTES_MERGE_PARTIAL."
msgstr "kunde inte hitta incheckning från NOTES_MERGE_PARTIAL."
@@ -9081,6 +9095,13 @@ msgstr ""
"git pack-objects [<flaggor>] <basnamn> [< <reflista> | < <objektlista>]"
#, c-format
+msgid "invalid --name-hash-version option: %d"
+msgstr "ogiltig flagga för --name-hash-version: %d"
+
+msgid "currently, --write-bitmap-index requires --name-hash-version=1"
+msgstr "--write-bitmap-index kräver för närvarande, --name-hash-version=1"
+
+#, c-format
msgid ""
"write_reuse_object: could not locate %s, expected at offset %<PRIuMAX> in "
"pack %s"
@@ -9190,14 +9211,14 @@ msgid ""
"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
"hash> <uri>' (got '%s')"
msgstr ""
-"värdet på uploadpack.blobpackfileuri måste vara på formen '<objekt-hash> "
-"<paket-hash> <uri>' (fick '%s')"
+"värdet på uploadpack.blobpackfileuri måste vara på formen ”<objekt-hash> "
+"<paket-hash> <uri>” (fick ”%s”)"
#, c-format
msgid ""
"object already configured in another uploadpack.blobpackfileuri (got '%s')"
msgstr ""
-"objektet redan konfigurerat i et annat uploadpack.blobpackfileuri (fick '%s)"
+"objektet redan konfigurerat i ett annat uploadpack.blobpackfileuri (fick ”%s”"
#, c-format
msgid "could not get type of object %s in pack %s"
@@ -9401,6 +9422,10 @@ msgid "exclude any configured uploadpack.blobpackfileuri with this protocol"
msgstr ""
"uteslut redan konfigurerade uploadpack.blobpackfileuri med detta protokoll"
+msgid "use the specified name-hash function to group similar objects"
+msgstr ""
+"använd den angivna namn-hash-funktionen för att gruppera liknande objekt"
+
#, c-format
msgid "delta chain depth %d is too deep, forcing %d"
msgstr "deltakedjedjupet %d är för djupt, påtvingar %d"
@@ -10189,7 +10214,7 @@ msgstr ""
"Ange vilken gren du vill ombasera mot.\n"
"Se git-rebase(1) för detaljer.\n"
"\n"
-" git rebase '<gren>'\n"
+" git rebase ”<gren>”\n"
"\n"
#, c-format
@@ -10603,7 +10628,8 @@ msgid "process the reflogs of all references"
msgstr "hantera referensloggar för alla referenser"
msgid "limits processing to reflogs from the current worktree only"
-msgstr "begränsar hantering av referensloggar till endast aktuell arbetskatalog"
+msgstr ""
+"begränsar hantering av referensloggar till endast aktuell arbetskatalog"
#, c-format
msgid "Marking reachable objects..."
@@ -10620,8 +10646,8 @@ msgstr "ingen referenslogg att ta bort angavs"
msgid "invalid ref format: %s"
msgstr "felaktigt referensformat: %s"
-msgid "git refs migrate --ref-format=<format> [--dry-run]"
-msgstr "git refs migrate --ref-format=<format> [--dry-run]"
+msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
+msgstr "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
msgid "git refs verify [--strict] [--verbose]"
msgstr "git refs verify [--strict] [--verbose]"
@@ -10632,6 +10658,9 @@ msgstr "ange referensformatet att konvertera till"
msgid "perform a non-destructive dry-run"
msgstr "utför ett icke-destruktiv testkörning"
+msgid "drop reflogs entirely during the migration"
+msgstr "kasta referensloggar helt under migreringen"
+
msgid "missing --ref-format=<format>"
msgstr "saknad --ref-format=<format>"
@@ -11091,8 +11120,14 @@ msgstr "Kommer inte ta bort alla icke-sänd-URL:er"
msgid "be verbose; must be placed before a subcommand"
msgstr "var pratsam; måste skrivas före ett underkommando"
-msgid "git repack [<options>]"
-msgstr "git repack [<flaggor>]"
+msgid ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
+msgstr ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<paket-namn>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
msgid ""
"Incremental repacks are incompatible with bitmap indexes. Use\n"
@@ -11166,6 +11201,12 @@ msgstr "sänd --no-reuse-delta till git-pack-objects"
msgid "pass --no-reuse-object to git-pack-objects"
msgstr "sänd --no-reuse-object till git-pack-objects"
+msgid ""
+"specify the name hash version to use for grouping similar objects by path"
+msgstr ""
+"ange den namnhash-version som ska användas för att gruppera liknande objekt "
+"efter sökväg"
+
msgid "do not run git-update-server-info"
msgstr "kör inte git-update-server-info"
@@ -11214,9 +11255,6 @@ msgstr "hitta ett geometrisk förlopp med faktor <N>"
msgid "write a multi-pack index of the resulting packs"
msgstr "skriv ett flerpaketsindex för de skapade paketen"
-msgid "pack prefix to store a pack containing pruned objects"
-msgstr "paketprefix att lagra ett paket som innehåller bortrensade objekt"
-
msgid "pack prefix to store a pack containing filtered out objects"
msgstr "paketprefix att lagra ett paket som innehåller utfiltrerade objekt"
@@ -11433,9 +11471,6 @@ 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"
@@ -12020,7 +12055,7 @@ msgid "<n>[,<base>]"
msgstr "<n>[,<bas>]"
msgid "show <n> most recent ref-log entries starting at base"
-msgstr "visa <n> nyaste refloggposter med början på bas"
+msgstr "visa <n> nyaste referensloggposter med början på bas"
msgid "no branches given, and HEAD is not valid"
msgstr "inga grenar angavs, och HEAD är inte giltigt"
@@ -12134,7 +12169,7 @@ msgid ""
"directory '%s' contains untracked files, but is not in the sparse-checkout "
"cone"
msgstr ""
-"katalogen ”%s” innehåller ospårade filer, men är inte i området som ages i "
+"katalogen ”%s” innehåller ospårade filer, men är inte i området som anges i "
"”sparse-checkout”"
#, c-format
@@ -12726,7 +12761,7 @@ msgstr "misslyckades klona ”%s” till undermodulsökvägen ”%s”"
#, c-format
msgid "could not get submodule directory for '%s'"
-msgstr "kunde inte få tag i undermodulkatalog för ”%s”"
+msgstr "kunde inte få tag i undermodulskatalog för ”%s”"
msgid "alternative anchor for relative paths"
msgstr "alternativa ankare för relativa sökvägar"
@@ -12830,8 +12865,8 @@ msgid ""
"Fetched in submodule path '%s', but it did not contain %s. Direct fetching "
"of that commit failed."
msgstr ""
-"Hämtade i undermodulssökvägen ”%s”, men den innehöll inte %s. Direkt "
-"hämtning av incheckningen misslyckades."
+"Hämtade i undermodulsökvägen ”%s”, men den innehöll inte %s. Direkt hämtning "
+"av incheckningen misslyckades."
#, c-format
msgid "could not initialize submodule at path '%s'"
@@ -13199,7 +13234,7 @@ msgid "replace the tag if exists"
msgstr "ersätt taggen om den finns"
msgid "create a reflog"
-msgstr "skapa en reflog"
+msgstr "skapa en referenslogg"
msgid "Tag listing options"
msgstr "Alternativ för listning av taggar"
@@ -13924,7 +13959,7 @@ msgstr "okänd kapabilitet ”%s”"
#, c-format
msgid "'%s' does not look like a v2 or v3 bundle file"
-msgstr "'%s' ser inte ut som en v2- eller v3-bunt-fil"
+msgstr "”%s” ser inte ut som en v2- eller v3-bunt-fil"
#, c-format
msgid "unrecognized header: %s%s (%d)"
@@ -14058,6 +14093,9 @@ msgstr "Importera ett GNU Arch-arkiv till Git"
msgid "Create an archive of files from a named tree"
msgstr "Skapa ett arkiv över filer från ett namngivet träd"
+msgid "Download missing objects in a partial clone"
+msgstr "Hämta saknade objekt i en delvis kloning"
+
msgid "Use binary search to find the commit that introduced a bug"
msgstr "Använd binärsökning för att hitta ändringen som introducerade ett fel"
@@ -14164,7 +14202,7 @@ msgid "Compare a tree to the working tree or index"
msgstr "Jämför en träd med arbetskatalogen eller indexet"
msgid "Compares the content and mode of blobs found via two tree objects"
-msgstr "Visar innehåll och läge för blobbar som hittats via två trädobjekt"
+msgstr "Visar innehåll och läge för blob:ar som hittats via två trädobjekt"
msgid "Show changes using common diff tools"
msgstr "Visa ändringar med vanliga diff-verktyg"
@@ -14996,19 +15034,19 @@ msgstr "kunde inte läsa katalogändringar [GLE %ld]"
#, c-format
msgid "opendir('%s') failed"
-msgstr "opendir('%s') misslyckades"
+msgstr "opendir(”%s”) misslyckades"
#, c-format
msgid "lstat('%s') failed"
-msgstr "lstat('%s') misslyckades"
+msgstr "lstat(”%s”) misslyckades"
#, c-format
msgid "strbuf_readlink('%s') failed"
-msgstr "strbuf_readlink('%s') misslyckades"
+msgstr "strbuf_readlink(”%s”) misslyckades"
#, c-format
msgid "closedir('%s') failed"
-msgstr "closedir('%s') misslyckades"
+msgstr "closedir(”%s”) misslyckades"
#, c-format
msgid "[GLE %ld] unable to open for read '%ls'"
@@ -15335,7 +15373,7 @@ msgstr "referensen ”%s” pekar inte på en blob"
#, c-format
msgid "unable to resolve config blob '%s'"
-msgstr "kan inte slå upp konfigurerings-blobben ”%s”"
+msgstr "kan inte slå upp konfigurerings-blob:en ”%s”"
msgid "unable to parse command-line config"
msgstr "kan inte tolka kommandoradskonfiguration"
@@ -15968,6 +16006,12 @@ msgstr "ogiltigt argument för %s"
msgid "invalid regex given to -I: '%s'"
msgstr "ogiltigt reguljärt uttryck angavs för -I: ”%s”"
+msgid "-G requires a non-empty argument"
+msgstr "-G kräver ett icke-tomt argument"
+
+msgid "-S requires a non-empty argument"
+msgstr "-S kräver ett icke-tomt argument"
+
#, c-format
msgid "failed to parse --submodule option parameter: '%s'"
msgstr "misslyckades tolka argument till flaggan --submodule: ”%s”"
@@ -16080,7 +16124,7 @@ msgid ""
"do not munge pathnames and use NULs as output field terminators in --raw or "
"--numstat"
msgstr ""
-"skriv inte om sökvägsnamn och använd NUL-tecken som fältseparerare i --raw "
+"skriv inte om sökvägsnamn och använd NUL-tecken som fältavdelare i --raw "
"eller --numstat"
msgid "<prefix>"
@@ -16495,7 +16539,7 @@ msgid "already have %s (%s)"
msgstr "har redan %s (%s)"
msgid "fetch-pack: unable to fork off sideband demultiplexer"
-msgstr "fetch-patch: kan inte grena (fork) av sidbandsmultiplexare"
+msgstr "fetch-patch: kan inte grena (fork) av sidbands-avmultiplexare"
msgid "protocol error: bad pack header"
msgstr "protokollfel: felaktigt packhuvud"
@@ -16689,7 +16733,7 @@ msgstr ""
"”git help -a” och ”git help -g” visar tillgängliga underkommandon och\n"
"några konceptvägledningar. Se ”git help <kommando>” eller ”git help\n"
"<koncept>” för att läsa mer om specifika underkommandon och koncept.\n"
-"See ”git help git” för en översikt över systemet."
+"Se ”git help git” för en översikt över systemet."
#, c-format
msgid "unsupported command listing type '%s'"
@@ -17020,7 +17064,7 @@ msgstr "inte ett git-arkiv"
#, c-format
msgid "argument to --packfile must be a valid hash (got '%s')"
msgstr ""
-"argumentet till --packfile måste vara ett giltigt hashvärde (fick '%s')"
+"argumentet till --packfile måste vara ett giltigt hashvärde (fick ”%s”)"
#, c-format
msgid "negative value for http.postBuffer; defaulting to %d"
@@ -17056,7 +17100,7 @@ msgid ""
" asked for: %s\n"
" redirect: %s"
msgstr ""
-"kan inte uppdatera urlbas från omdirigerin:\n"
+"kan inte uppdatera urlbas från omdirigering:\n"
" bad om: %s\n"
" omdirigering: %s"
@@ -17914,7 +17958,7 @@ 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"
msgid "multi-pack-index large offset out of bounds"
-msgstr "stort offset för mult-pack-index utanför gränsen"
+msgstr "stort offset för multi-pack-index utanför gränsen"
msgid "multi-pack-index file exists, but failed to parse"
msgstr "multi-pack-indexfilen finns, men kunde inte tolkas"
@@ -17985,7 +18029,7 @@ msgstr "Kan inte checka in oinitierat/orefererat anteckningsträd"
#, c-format
msgid "Bad notes.rewriteMode value: '%s'"
-msgstr "Felaktigt värde för notes.rewriteMode: '%s'"
+msgstr "Felaktigt värde för notes.rewriteMode: ”%s”"
#, c-format
msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
@@ -18135,6 +18179,10 @@ msgid "unable to write file %s"
msgstr "kan inte skriva filen %s"
#, c-format
+msgid "unable to write repeatedly vanishing file %s"
+msgstr "kan inte skriva till filen %s som hela tiden försvinner"
+
+#, c-format
msgid "unable to set permission to '%s'"
msgstr "kan inte sätta behörigheten till ”%s”"
@@ -18538,7 +18586,8 @@ msgstr "bitkarteresultat stämmer inte överens"
#, c-format
msgid "pseudo-merge index out of range (%<PRIu32> >= %<PRIuMAX>)"
-msgstr "pseudosammanslagningsindex utanför intervallet (%<PRIu32> ≥ %<PRIuMAX>)"
+msgstr ""
+"pseudosammanslagningsindex utanför intervallet (%<PRIu32> ≥ %<PRIuMAX>)"
#, c-format
msgid "could not find '%s' in pack '%s' at offset %<PRIuMAX>"
@@ -18694,6 +18743,52 @@ msgstr "okänd flagga ”%c”"
msgid "unknown non-ascii option in string: `%s'"
msgstr "okänd icke-ascii-flagga i strängen: ”%s”"
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the long form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[=<%s>]"
+msgstr "[=<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the short form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[<%s>]"
+msgstr "[<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string stands for a
+#. value given to a command line option, and "<>" is there
+#. as a convention to signal that it is a placeholder
+#. (i.e. the user should substitute it with the real value).
+#. If your language uses a different convention, you can
+#. change "<%s>" part to match yours, e.g. it might use
+#. "|%s|" instead, or if the alphabet is different enough it
+#. may use "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid " <%s>"
+msgstr " <%s>"
+
msgid "..."
msgstr "..."
@@ -18779,6 +18874,21 @@ msgid "failed to parse %s"
msgstr "misslyckades tolka %s"
#, c-format
+msgid "failed to walk children of tree %s: not found"
+msgstr "misslyckades traversera löven i trädet %s: hittades inte"
+
+#, c-format
+msgid "failed to find object %s"
+msgstr "misslyckades hitta objektet %s"
+
+#, c-format
+msgid "failed to find tag %s"
+msgstr "misslyckades hitta taggen %s"
+
+msgid "failed to setup revision walk"
+msgstr "misslyckades starta revisionstraversering"
+
+#, c-format
msgid "Could not make %s writable by group"
msgstr "Kunde inte göra %s skrivbar för gruppen"
@@ -18923,6 +19033,22 @@ msgstr "kontraktsfjärr kan inte börja med ”/”: %s"
msgid "could not fetch %s from promisor remote"
msgstr "kunde inte hämta %s från kontraktsfjärr"
+#, c-format
+msgid "known remote named '%s' but with url '%s' instead of '%s'"
+msgstr "känd fjärr som heter ”%s” med med url:en ”%s” istället för ”%s”"
+
+#, c-format
+msgid "unknown '%s' value for '%s' config option"
+msgstr "okänt värde ”%s” för inställningen ”%s”"
+
+#, c-format
+msgid "unknown element '%s' from remote info"
+msgstr "okänt värde ”%s” från fjärrinformation"
+
+#, c-format
+msgid "accepted promisor remote '%s' not found"
+msgstr "godkänd kontraktsfjärr ”%s” hittades inte"
+
msgid "object-info: expected flush after arguments"
msgstr "object-info: förväntade ”flush” efter argument"
@@ -19248,7 +19374,7 @@ msgstr ""
" enrads, om inget incheckningsmeddelande angavs); använd\n"
" -c <incheckning> för att skriva om meddelandet.\n"
"u, update-ref <ref> = spåra en platshållare för <ref> att uppdatera\n"
-" till denna position bland nya inchecknngar.\n"
+" till denna position bland nya incheckningar.\n"
" <ref> uppdateras i slutet av ombaseringen.\n"
"\n"
"Du kan byta ordning på raderna; de utförs uppifrån och ned.\n"
@@ -19616,7 +19742,7 @@ msgid "refusing to update ref with bad name '%s'"
msgstr "vägrar uppdatera referens med trasigt namn ”%s”"
msgid "refusing to force and skip creation of reflog"
-msgstr "vägrar att tvinga och hoppa över skapande av reflogg"
+msgstr "vägrar att tvinga och hoppa över skapande av referenslogg"
#, c-format
msgid "update_ref failed for ref '%s': %s"
@@ -19749,6 +19875,14 @@ msgid "invalid refspec '%s'"
msgstr "felaktig referensspecifikation: ”%s”"
#, c-format
+msgid "pattern '%s' has no '*'"
+msgstr "mönstret ”%s” innehåller ingen ”*”"
+
+#, c-format
+msgid "replacement '%s' has no '*'"
+msgstr "ersättningen ”%s” innehåller ingen ”*”"
+
+#, c-format
msgid "invalid quoting in push-option value: '%s'"
msgstr "felaktig citering på värde för push-option: ”%s”"
@@ -19867,6 +20001,28 @@ msgid "remote-curl: unknown command '%s' from git"
msgstr "remote-curl: okänt kommando ”%s” från git"
#, c-format
+msgid ""
+"reading remote from \"%s/%s\", which is nominated for removal.\n"
+"\n"
+"If you still use the \"remotes/\" directory it is recommended to\n"
+"migrate to config-based remotes:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"If you cannot, please let us know why you still need to use it by\n"
+"sending an e-mail to <git@vger.kernel.org>."
+msgstr ""
+"läser fjärren från ”%s/%s”, som har nominerats för borttagning.\n"
+"\n"
+"Om du fortfarande använder ”remotes/”-katalogen rekommenderas du\n"
+"migrera till konfigurationsbaserade fjärrar:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"Om du inte kan det, berätta för oss varför du fortfarande behöver\n"
+"använda det på e-post till <git@vger.kernel.org>."
+
+#, c-format
msgid "config remote shorthand cannot begin with '/': %s"
msgstr "konfigurerad kortform för fjärr kan inte börja med ”/”: %s"
@@ -19901,14 +20057,6 @@ msgid "%s tracks both %s and %s"
msgstr "%s spårar både %s och %s"
#, c-format
-msgid "key '%s' of pattern had no '*'"
-msgstr "nyckeln ”%s” i mönstret innehåller ingen ”*”"
-
-#, c-format
-msgid "value '%s' of pattern has no '*'"
-msgstr "värdet ”%s” i mönstret innehåller ingen ”*”"
-
-#, c-format
msgid "src refspec %s does not match any"
msgstr "käll-referensspecifikationen %s motsvarar ingen"
@@ -21798,6 +21946,27 @@ 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 "test-tool path-walk <options> -- <revision-options>"
+msgstr "test-tool path-walk <flaggor> -- <revision-flaggor>"
+
+msgid "toggle inclusion of blob objects"
+msgstr "växla om blob-objekt ska vara med eller inte"
+
+msgid "toggle inclusion of commit objects"
+msgstr "växla om incheckningsobjekt ska vara med eller inte"
+
+msgid "toggle inclusion of tag objects"
+msgstr "växla om taggobjekt ska vara med eller inte"
+
+msgid "toggle inclusion of tree objects"
+msgstr "växla om trädobjekt ska vara med eller inte"
+
+msgid "toggle pruning of uninteresting paths"
+msgstr "växla bortrensning av ointressanta sökvägar"
+
+msgid "read a pattern list over stdin"
+msgstr "läs en mönsterlista från standard in"
+
#, c-format
msgid "commit %s is not marked reachable"
msgstr "incheckning %s är inte märkt nåbar"
@@ -22431,6 +22600,10 @@ msgstr "fel: "
msgid "warning: "
msgstr "varning: "
+#, c-format
+msgid "uname() failed with error '%s' (%d)\n"
+msgstr "uname() misslyckades med felet ”%s” (%d)\n"
+
msgid "Fetching objects"
msgstr "Hämtar objekt"
@@ -23265,7 +23438,7 @@ msgstr "Nödvändig SMTP-server har inte angivits korrekt."
#, perl-format
msgid "Server does not support STARTTLS! %s"
-msgstr "Servern stöder inte SMARTTLS! %s"
+msgstr "Servern stöder inte STARTTLS! %s"
#, perl-format
msgid "STARTTLS failed! %s"
diff --git a/po/tr.po b/po/tr.po
index 1bc43453aa..4ed91cbd97 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -96,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: 2024-12-23 18:57+0000\n"
-"PO-Revision-Date: 2025-01-01 15:00+0300\n"
+"POT-Creation-Date: 2025-03-11 15:01+0300\n"
+"PO-Revision-Date: 2025-03-11 15: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"
@@ -2408,6 +2408,18 @@ msgstr "git archive: Protokol hatası"
msgid "git archive: expected a flush"
msgstr "git archive: Floş bekleniyordu"
+msgid "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+msgstr "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+
+msgid "problem loading sparse-checkout"
+msgstr "aralıklı çıkış yüklenirken sorun"
+
+msgid "Minimum number of objects to request at a time"
+msgstr "Bir kerede istenecek en çok nesne sayısı"
+
+msgid "Restrict the missing objects to the current sparse-checkout"
+msgstr "Eksik nesneleri geçerli aralıklı çıkışa sınırla"
+
msgid ""
"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>] [--no-"
"checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]"
@@ -3149,10 +3161,6 @@ msgstr ""
msgid "git version:\n"
msgstr "git sürümü:\n"
-#, c-format
-msgid "uname() failed with error '%s' (%d)\n"
-msgstr "uname() '%s' hatasını verip çıktı (%d)\n"
-
msgid "compiler info: "
msgstr "derleyici bilgisi: "
@@ -4158,8 +4166,91 @@ msgstr ""
"clean.requireForce 'true' olarak ayarlı ve -f verilmedi; temizlik "
"reddediliyor"
-msgid "git clone [<options>] [--] <repo> [<dir>]"
-msgstr "git clone [<seçenekler>] [--] <depo> [<dizin>]"
+#, c-format
+msgid "info: Could not add alternate for '%s': %s\n"
+msgstr "info: '%s' için alternatif eklenemedi: %s\n"
+
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "'%s' dosyasının bilgileri alınamadı"
+
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s var ve bir dizin değil"
+
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "'%s' bir sembolik bağ; --local ile klonlama reddediliyor"
+
+#, c-format
+msgid "failed to start iterator over '%s'"
+msgstr "yineleyici '%s' üzerinden çalıştırılamadı"
+
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr "'%s' sembolik bağı var, --local ile klonlama reddediliyor"
+
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "'%s' bağlantısı kesilemedi"
+
+#, c-format
+msgid "hardlink cannot be checked at '%s'"
+msgstr "sabit bağlantı, '%s' konumunda denetlenemiyor"
+
+#, c-format
+msgid "hardlink different from source at '%s'"
+msgstr "sabit bağlantı, '%s' konumundaki kaynaktan farklı"
+
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "'%s' bağı oluşturulamadı"
+
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "dosya şuraya kopyalanamadı: '%s'"
+
+#, c-format
+msgid "failed to iterate over '%s'"
+msgstr "'%s' üzerinde yinelenemedi"
+
+#, c-format
+msgid "done.\n"
+msgstr "bitti.\n"
+
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry with 'git restore --source=HEAD :/'\n"
+msgstr ""
+"Klonlama başarılı oldu; ancak çıkış yapılamadı.\n"
+"Neyin çıkış yapılıp yapılmadığını 'git status' ile inceleyebilir\n"
+"ve 'git restore --source=HEAD' ile yeniden deneyebilirsiniz.\n"
+
+msgid "remote did not send all necessary objects"
+msgstr "uzak konum gereken tüm nesneleri göndermedi"
+
+#, c-format
+msgid "unable to update %s"
+msgstr "%s güncellenemiyor"
+
+msgid "failed to initialize sparse-checkout"
+msgstr "sparse-checkout ilklendirilemedi"
+
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
+msgstr "uzak konum HEAD'i, var olmayan başvuruya başvuruyor; çıkış yapılamıyor"
+
+msgid "unable to checkout working tree"
+msgstr "çalışma ağacı çıkış yapılamıyor"
+
+msgid "unable to write parameters to config file"
+msgstr "parametreler yapılandırma dosyasına yazılamıyor"
+
+msgid "cannot repack to clean up"
+msgstr "temizlik için yeniden paketlenemiyor"
+
+msgid "cannot unlink temporary alternates file"
+msgstr "geçici alternatifler dosyasının bağlantısı kesilemiyor"
msgid "don't clone shallow repository"
msgstr "sığ depoyu klonlama"
@@ -4212,6 +4303,9 @@ msgstr "üstkaynağı izlemek için 'origin' yerine <ad> kullan"
msgid "checkout <branch> instead of the remote's HEAD"
msgstr "uzak konumun HEAD'i yerine <dal>'ı çıkış yap"
+msgid "clone single revision <rev> and check out"
+msgstr "tek revizyonlu <rev>'i klonla ve çıkış yap"
+
msgid "path to git-upload-pack on the remote"
msgstr "uzak konumdaki git-upload-pack'e olan yol"
@@ -4233,8 +4327,8 @@ msgstr "başvuru hariç tutarak sığ klonun geçmişini derinleştir"
msgid "clone only one branch, HEAD or --branch"
msgstr "yalnızca bir dal klonla, HEAD veya --branch"
-msgid "don't clone any tags, and make later fetches not to follow them"
-msgstr "etiket klonlama ve sonraki getirmeler de onları izlemesin"
+msgid "clone tags, and make later fetches not to follow them"
+msgstr "etiketleri klonla ve sonraki getirmelerin onları izlememesini sağla"
msgid "any cloned submodules will be shallow"
msgstr "klonlanan altmodüller sığ olacak"
@@ -4277,95 +4371,8 @@ msgstr "uri"
msgid "a URI for downloading bundles before fetching from origin remote"
msgstr "uzak konum kökeninden getirmeden önce demetleri indirmek için bir URI"
-#, c-format
-msgid "info: Could not add alternate for '%s': %s\n"
-msgstr "info: '%s' için alternatif eklenemedi: %s\n"
-
-#, c-format
-msgid "failed to stat '%s'"
-msgstr "'%s' dosyasının bilgileri alınamadı"
-
-#, c-format
-msgid "%s exists and is not a directory"
-msgstr "%s var ve bir dizin değil"
-
-#, c-format
-msgid "'%s' is a symlink, refusing to clone with --local"
-msgstr "'%s' bir sembolik bağ; --local ile klonlama reddediliyor"
-
-#, c-format
-msgid "failed to start iterator over '%s'"
-msgstr "yineleyici '%s' üzerinden çalıştırılamadı"
-
-#, c-format
-msgid "symlink '%s' exists, refusing to clone with --local"
-msgstr "'%s' sembolik bağı var, --local ile klonlama reddediliyor"
-
-#, c-format
-msgid "failed to unlink '%s'"
-msgstr "'%s' bağlantısı kesilemedi"
-
-#, c-format
-msgid "hardlink cannot be checked at '%s'"
-msgstr "sabit bağlantı, '%s' konumunda denetlenemiyor"
-
-#, c-format
-msgid "hardlink different from source at '%s'"
-msgstr "sabit bağlantı, '%s' konumundaki kaynaktan farklı"
-
-#, c-format
-msgid "failed to create link '%s'"
-msgstr "'%s' bağı oluşturulamadı"
-
-#, c-format
-msgid "failed to copy file to '%s'"
-msgstr "dosya şuraya kopyalanamadı: '%s'"
-
-#, c-format
-msgid "failed to iterate over '%s'"
-msgstr "'%s' üzerinde yinelenemedi"
-
-#, c-format
-msgid "done.\n"
-msgstr "bitti.\n"
-
-msgid ""
-"Clone succeeded, but checkout failed.\n"
-"You can inspect what was checked out with 'git status'\n"
-"and retry with 'git restore --source=HEAD :/'\n"
-msgstr ""
-"Klonlama başarılı oldu; ancak çıkış yapılamadı.\n"
-"Neyin çıkış yapılıp yapılmadığını 'git status' ile inceleyebilir\n"
-"ve 'git restore --source=HEAD' ile yeniden deneyebilirsiniz.\n"
-
-#, c-format
-msgid "Could not find remote branch %s to clone."
-msgstr "Klonlanacak %s uzak dal bulunamadı."
-
-msgid "remote did not send all necessary objects"
-msgstr "uzak konum gereken tüm nesneleri göndermedi"
-
-#, c-format
-msgid "unable to update %s"
-msgstr "%s güncellenemiyor"
-
-msgid "failed to initialize sparse-checkout"
-msgstr "sparse-checkout ilklendirilemedi"
-
-msgid "remote HEAD refers to nonexistent ref, unable to checkout"
-msgstr "uzak konum HEAD'i, var olmayan başvuruya başvuruyor; çıkış yapılamıyor"
-
-msgid "unable to checkout working tree"
-msgstr "çalışma ağacı çıkış yapılamıyor"
-
-msgid "unable to write parameters to config file"
-msgstr "parametreler yapılandırma dosyasına yazılamıyor"
-
-msgid "cannot repack to clean up"
-msgstr "temizlik için yeniden paketlenemiyor"
-
-msgid "cannot unlink temporary alternates file"
-msgstr "geçici alternatifler dosyasının bağlantısı kesilemiyor"
+msgid "git clone [<options>] [--] <repo> [<dir>]"
+msgstr "git clone [<seçenekler>] [--] <depo> [<dizin>]"
msgid "Too many arguments."
msgstr "Çok fazla argüman."
@@ -4465,6 +4472,10 @@ msgstr "uzak konum taşıması hata bildirdi"
msgid "Remote branch %s not found in upstream %s"
msgstr "%s uzak dalı %s üstkaynağında bulunamadı"
+#, c-format
+msgid "Remote revision %s not found in upstream %s"
+msgstr "%s uzak revizyonu, %s üstkaynağında bulunamadı"
+
msgid "You appear to have cloned an empty repository."
msgstr "Boş bir depoyu klonlamış görünüyorsunuz."
@@ -4641,7 +4652,7 @@ msgid "git commit-tree: failed to read"
msgstr "git commit-tree: okunamadı"
msgid ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
"reword):]<commit>]\n"
" [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
@@ -4651,11 +4662,11 @@ msgid ""
" [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
" [--] [<pathspec>...]"
msgstr ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<kip>] [--amend]\n"
-" [--dry-run] [(-c | -C | --squash) <işleme> | --fixup\n"
-" [(amend|reword):]<işleme>] [-F <dosya> | -m <ileti>] [--"
-"reset-author] [--allow-empty]\n"
-" [--allow-empty-message] [--no-verify] [-e] [--author=<author>]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<kip>]] [--amend]\n"
+" [--dry-run] [(-c | -C | --squash) <işleme> |\n"
+" --fixup [(amend|reword):]<işleme>]\n"
+" [-F <dosya> | -m <ileti>] [--reset-author] [--allow-empty]\n"
+" [--allow-empty-message] [--no-verify] [-e] [--author=<yazar>]\n"
" [--date=<tarih>] [--cleanup=<kip>] [--[no-]status]\n"
" [-i | -o] [--pathspec-from-file=<dosya> [--pathspec-file-nul]]\n"
" [(--trailer <jeton>[(=|:)<değer>])...] [-S[<anahtar-kimliği>]]\n"
@@ -6044,15 +6055,15 @@ msgid ""
"Run 'git remote set-head %s %s' to follow the change, or set\n"
"'remote.%s.followRemoteHEAD' configuration option to a different value\n"
"if you do not want to see this message. Specifically running\n"
-"'git config set remote.%s.followRemoteHEAD %s' will disable the warning\n"
-"until the remote changes HEAD to something else."
+"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n"
+"will disable the warning until the remote changes HEAD to something else."
msgstr ""
"Değişikliği izlemek için 'git remote set-head %s %s' yapın veya\n"
"'remote.%s.followRemoteHEAD' yapılandırma seçeneğini başka bir\n"
"değere ayarlayın (bu iletiyi görmek istemiyorsanız). Özellikle\n"
-"'git config set remote.%s.followRemoteHEAD %s' komutunu çalıştırmak\n"
-"uyarıyı HEAD'e veya başka bir şeye uzaktan değişiklik olana dek\n"
-"devre dışı bırakır."
+"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n"
+"komutunu çalıştırmak, uyarıyı HEAD'e veya başka bir şeye uzaktan\n"
+"değişiklik olana dek devre dışı bırakır."
msgid "multiple branches detected, incompatible with --set-upstream"
msgstr "birden çok dal algılandı, --set-upstream ile uyumsuz"
@@ -6729,6 +6740,9 @@ msgstr "başka bir gc çalışıyor olsa bile zorla gc çalıştır"
msgid "repack all other packs except the largest pack"
msgstr "en büyük paket dışındaki diğer tüm paketleri yeniden paketle"
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr "budanan nesneler içeren paketi depolamak için paket öneki"
+
#, c-format
msgid "failed to parse gc.logExpiry value %s"
msgstr "gc.logExpiry değeri %s ayrıştırılamadı"
@@ -7526,6 +7540,10 @@ msgid "Cannot come back to cwd"
msgstr "Geçerli çalışma dizinine geri gelinemiyor"
#, c-format
+msgid "bad --pack_header: %s"
+msgstr "hatalı --pack_header: %s"
+
+#, c-format
msgid "bad %s"
msgstr "hatalı %s"
@@ -8384,10 +8402,6 @@ msgstr "bilinmeyen strateji seçeneği: -X%s"
msgid "malformed input line: '%s'."
msgstr "hatalı oluşturulmuş girdi satırı: '%s'."
-#, c-format
-msgid "merging cannot continue; got unclean result of %d"
-msgstr "birleştirme sürdürülemiyor; %d için temiz olmayan sonuçlar alındı"
-
msgid "git merge [<options>] [<commit>...]"
msgstr "git merge [<seçenekler>] [<işleme>...]"
@@ -9202,6 +9216,13 @@ msgstr ""
"listesi>]"
#, c-format
+msgid "invalid --name-hash-version option: %d"
+msgstr "geçersiz --name-hash-version seçeneği: %d"
+
+msgid "currently, --write-bitmap-index requires --name-hash-version=1"
+msgstr "şu anda --write-bitmap-index, --name-hash-version=1 gerektiriyor"
+
+#, c-format
msgid ""
"write_reuse_object: could not locate %s, expected at offset %<PRIuMAX> in "
"pack %s"
@@ -9526,6 +9547,9 @@ msgstr ""
"bu protokol ile herhangi bir yapılandırılmış uploadpack.blobpackfileuri "
"ögesini hariç tut"
+msgid "use the specified name-hash function to group similar objects"
+msgstr "benzer nesneleri gruplamak için belirtilen name-hash işlevini kullan"
+
#, c-format
msgid "delta chain depth %d is too deep, forcing %d"
msgstr "delta zincir derinliği %d çok derin, %d zorlanıyor"
@@ -10756,8 +10780,8 @@ msgstr "silmek için bir başvuru günlüğü belirtilmedi"
msgid "invalid ref format: %s"
msgstr "geçersiz başvuru biçimi: %s"
-msgid "git refs migrate --ref-format=<format> [--dry-run]"
-msgstr "git refs migrate --ref-format=<biçim> [--dry-run]"
+msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
+msgstr "git refs migrate --ref-format=<biçim> [--no-reflog] [--dry-run]"
msgid "git refs verify [--strict] [--verbose]"
msgstr "git refs verify [--strict] [--verbose]"
@@ -10768,6 +10792,9 @@ msgstr "dönüştürülecek başvuru biçimini belirt"
msgid "perform a non-destructive dry-run"
msgstr "yıkıcı olmayan bir deneme gerçekleştir"
+msgid "drop reflogs entirely during the migration"
+msgstr "göç sırasında başvuru günlüklerini tümüyle bırak"
+
msgid "missing --ref-format=<format>"
msgstr "--ref-format=<biçim> eksik"
@@ -11234,8 +11261,14 @@ msgstr "Tüm itme olmayan URL'ler silinmeyecek"
msgid "be verbose; must be placed before a subcommand"
msgstr "ayrıntılı anlat; bir altkomuttan önce yerleştirilmelidir"
-msgid "git repack [<options>]"
-msgstr "git repack [<seçenekler>]"
+msgid ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
+msgstr ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<paket-adı>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
msgid ""
"Incremental repacks are incompatible with bitmap indexes. Use\n"
@@ -11309,6 +11342,11 @@ msgstr "'git-pack-objects'e --no-reuse-delta geçir"
msgid "pass --no-reuse-object to git-pack-objects"
msgstr "'git-pack-objects'e --no-reuse-object geçir"
+msgid ""
+"specify the name hash version to use for grouping similar objects by path"
+msgstr ""
+"benzer nesneleri yola göre gruplamada kullanılacak name-hash sürümünü belirt"
+
msgid "do not run git-update-server-info"
msgstr "'git-update-server-info' çalıştırma"
@@ -11357,9 +11395,6 @@ msgstr "<N> faktörlü bir geometrik ilerleme bul"
msgid "write a multi-pack index of the resulting packs"
msgstr "ortaya çıkan paketlerin bir çoklu paket indeksini yaz"
-msgid "pack prefix to store a pack containing pruned objects"
-msgstr "budanan nesneler içeren paketi depolamak için paket öneki"
-
msgid "pack prefix to store a pack containing filtered out objects"
msgstr "süzülen nesneler içeren paketi depolamak için paket öneki"
@@ -11576,9 +11611,6 @@ 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ı"
@@ -14200,6 +14232,9 @@ msgstr "Git'e bir GNU Arch deposu içe aktar"
msgid "Create an archive of files from a named tree"
msgstr "Ad verilmiş ağaçtan bir dosyalar arşivi oluştur"
+msgid "Download missing objects in a partial clone"
+msgstr "Eksik nesneleri kısımsal bir klonda indir"
+
msgid "Use binary search to find the commit that introduced a bug"
msgstr "Hatalara neden olan işlemeyi bulmada ikili arama kullan"
@@ -16111,6 +16146,12 @@ msgstr "%s için geçersiz argüman"
msgid "invalid regex given to -I: '%s'"
msgstr "-I'ya geçersiz düzenli ifade verildi: '%s'"
+msgid "-G requires a non-empty argument"
+msgstr "-G, boş olmayan bir argüman gerektiriyor"
+
+msgid "-S requires a non-empty argument"
+msgstr "-S, boş olmayan bir argüman gerektiriyor"
+
#, c-format
msgid "failed to parse --submodule option parameter: '%s'"
msgstr "--submodule seçenek parametresi ayrıştırılamadı: '%s'"
@@ -18290,6 +18331,10 @@ msgid "unable to write file %s"
msgstr "%s dosyası yazılamıyor"
#, c-format
+msgid "unable to write repeatedly vanishing file %s"
+msgstr "sürekli olarak kaybolan %s dosyası yazılamıyor"
+
+#, c-format
msgid "unable to set permission to '%s'"
msgstr "'%s' ögesine izin ayarlanamıyor"
@@ -18851,6 +18896,52 @@ msgstr "bilinmeyen anahtar '%c'"
msgid "unknown non-ascii option in string: `%s'"
msgstr "dizi içinde bilinmeyen ascii dışı seçenek: '%s'"
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the long form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[=<%s>]"
+msgstr "[=<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the short form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[<%s>]"
+msgstr "[<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string stands for a
+#. value given to a command line option, and "<>" is there
+#. as a convention to signal that it is a placeholder
+#. (i.e. the user should substitute it with the real value).
+#. If your language uses a different convention, you can
+#. change "<%s>" part to match yours, e.g. it might use
+#. "|%s|" instead, or if the alphabet is different enough it
+#. may use "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid " <%s>"
+msgstr " <%s>"
+
msgid "..."
msgstr "..."
@@ -18937,6 +19028,21 @@ msgid "failed to parse %s"
msgstr "%s ayrıştırılamadı"
#, c-format
+msgid "failed to walk children of tree %s: not found"
+msgstr "%s ağacının alt ögeleri yürütülemedi: bulunamadı"
+
+#, c-format
+msgid "failed to find object %s"
+msgstr "%s nesnesi bulunamadı"
+
+#, c-format
+msgid "failed to find tag %s"
+msgstr "%s etiketi bulunamadı"
+
+msgid "failed to setup revision walk"
+msgstr "revizyon yürüyüşü ayarlanamadı"
+
+#, c-format
msgid "Could not make %s writable by group"
msgstr "%s grup ile yazılabilir yapılamadı"
@@ -19079,6 +19185,22 @@ msgstr "vaatçi uzak konum adı '/' ile başlayamaz: %s"
msgid "could not fetch %s from promisor remote"
msgstr "vaatçi uzak konumundan %s getirilemedi"
+#, c-format
+msgid "known remote named '%s' but with url '%s' instead of '%s'"
+msgstr "bilinen uzak konum adı '%s'; ancak url'si '%s'; '%s' olmalı"
+
+#, c-format
+msgid "unknown '%s' value for '%s' config option"
+msgstr "'%s' yapılandırma seçeneği için bilinmeyen değer: '%s'"
+
+#, c-format
+msgid "unknown element '%s' from remote info"
+msgstr "uzak bilgiden bilinmeyen öge '%s'"
+
+#, c-format
+msgid "accepted promisor remote '%s' not found"
+msgstr "kabul edilmiş vaatçi uzak konum '%s' bulunamadı"
+
msgid "object-info: expected flush after arguments"
msgstr "object-info: argümanlardan sonra floş bekleniyordu"
@@ -19902,6 +20024,14 @@ msgid "invalid refspec '%s'"
msgstr "geçersiz başvuru belirteci '%s'"
#, c-format
+msgid "pattern '%s' has no '*'"
+msgstr "'%s' dizgisinde '*' yok"
+
+#, c-format
+msgid "replacement '%s' has no '*'"
+msgstr "'%s' yedeğinde '*' yok"
+
+#, c-format
msgid "invalid quoting in push-option value: '%s'"
msgstr "push-option değerinde geçersiz tırnak içine alım: '%s'"
@@ -20021,6 +20151,31 @@ msgid "remote-curl: unknown command '%s' from git"
msgstr "remote-curl: git'ten bilinmeyen komut '%s'"
#, c-format
+msgid ""
+"reading remote from \"%s/%s\", which is nominated for removal.\n"
+"\n"
+"If you still use the \"remotes/\" directory it is recommended to\n"
+"migrate to config-based remotes:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"If you cannot, please let us know why you still need to use it by\n"
+"sending an e-mail to <git@vger.kernel.org>."
+msgstr ""
+"Kaldırma için aday gösterilmiş \"%s/%s\" konumundan\n"
+"uzak konum okunuyor.\n"
+"\n"
+"Eğer hâlâ \"remotes\" dizinini kullanıyorsanız\n"
+"yapılandırma tabanlı uzak konumlara geçiş yapmanız\n"
+"önerilir:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"Eğer geçiş yapamıyorsanız bunu neden kullandığınıza\n"
+"dair bir iletiyi <git@vger.kernel.org> adresine\n"
+"gönderin."
+
+#, c-format
msgid "config remote shorthand cannot begin with '/': %s"
msgstr "uzak konum yapılandırma stenografisi '/' ile başlayamaz: %s"
@@ -20055,14 +20210,6 @@ msgid "%s tracks both %s and %s"
msgstr "%s hem %s hem %s ögelerini izler"
#, c-format
-msgid "key '%s' of pattern had no '*'"
-msgstr "dizginin '%s' anahtarında '*' yoktu"
-
-#, c-format
-msgid "value '%s' of pattern has no '*'"
-msgstr "dizginin '%s' değerinde '*' yok"
-
-#, c-format
msgid "src refspec %s does not match any"
msgstr "kaynak başvuru belirteci %s başka hiçbir şeyle eşleşmiyor"
@@ -21948,6 +22095,27 @@ 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 "test-tool path-walk <options> -- <revision-options>"
+msgstr "test-tool path-walk <seçenekler> -- <revizyon-seçenekleri>"
+
+msgid "toggle inclusion of blob objects"
+msgstr "ikili nesnelerin içerilmesini aç/kapat"
+
+msgid "toggle inclusion of commit objects"
+msgstr "işleme nesnelerinin içerilmesini aç/kapat"
+
+msgid "toggle inclusion of tag objects"
+msgstr "etiket nesnelerinin içerilmesini aç/kapat"
+
+msgid "toggle inclusion of tree objects"
+msgstr "ağaç nesnelerinin içerilmesini aç/kapat"
+
+msgid "toggle pruning of uninteresting paths"
+msgstr "ilgisiz yolların budanmasını aç/kapat"
+
+msgid "read a pattern list over stdin"
+msgstr "stdin'den bir dizgi listesi oku"
+
#, c-format
msgid "commit %s is not marked reachable"
msgstr "%s işlemesi ulaşılabilir olarak imlenmedi"
@@ -22575,6 +22743,10 @@ msgstr "hata: "
msgid "warning: "
msgstr "uyarı: "
+#, c-format
+msgid "uname() failed with error '%s' (%d)\n"
+msgstr "uname() '%s' hatasını verip çıktı (%d)\n"
+
msgid "Fetching objects"
msgstr "Nesneler getiriliyor"
diff --git a/po/uk.po b/po/uk.po
index 5f68f36c7d..39c22289fa 100644
--- a/po/uk.po
+++ b/po/uk.po
@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Git v2.46\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2025-01-04 19:26-0800\n"
-"PO-Revision-Date: 2025-01-03 14:17-0800\n"
+"POT-Creation-Date: 2025-03-09 15:40-0700\n"
+"PO-Revision-Date: 2025-03-09 16:53-0700\n"
"Last-Translator: Kateryna Golovanova <kate@kgthreads.com>\n"
"Language-Team: Ukrainian <https://github.com/arkid15r/git-uk-l10n/>\n"
"Language: uk\n"
@@ -1433,7 +1433,7 @@ msgid "write the archive to this file"
msgstr "записати архів до цього файлу"
msgid "read .gitattributes in working directory"
-msgstr "прочитати .gitattributes робочої директорії"
+msgstr "читати .gitattributes робочої директорії"
msgid "report archived files on stderr"
msgstr "звітувати про заархівовані файли в stderr"
@@ -2357,6 +2357,18 @@ msgstr "git archive: помилка протоколу"
msgid "git archive: expected a flush"
msgstr "git archive: очікувалось flush"
+msgid "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+msgstr "git backfill [--min-batch-size=<н>] [--[no-]sparse]"
+
+msgid "problem loading sparse-checkout"
+msgstr "проблема із завантаженням розрідженого переходу"
+
+msgid "Minimum number of objects to request at a time"
+msgstr "Мінімальна кількість обʼєктів для запиту за один раз"
+
+msgid "Restrict the missing objects to the current sparse-checkout"
+msgstr "Обмежити відсутні обʼєкти поточним розрідженим переходом"
+
msgid ""
"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>] [--no-"
"checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]"
@@ -3117,15 +3129,11 @@ msgstr ""
msgid "git version:\n"
msgstr "версія git:\n"
-#, c-format
-msgid "uname() failed with error '%s' (%d)\n"
-msgstr "uname() завершився невдало з помилкою \"%s\" (%d)\n"
-
msgid "compiler info: "
msgstr "інформація щодо компілятора: "
msgid "libc info: "
-msgstr "информація щодо libc: "
+msgstr "інформація щодо libc: "
msgid "not run from a git repository - no hooks to show\n"
msgstr "запущено не з git сховища - немає гачків для показу\n"
@@ -3448,7 +3456,7 @@ msgid "use .gitattributes only from the index"
msgstr "використовувати .gitattributes тільки з індексу"
msgid "read file names from stdin"
-msgstr "зчитувати назви файлів з stdin"
+msgstr "читати назви файлів з stdin"
msgid "terminate input and output records by a NUL character"
msgstr "завершувати вхідні та вихідні записи символом NUL"
@@ -3493,13 +3501,13 @@ msgid "also read contacts from stdin"
msgstr "також читати контакти з stdin"
msgid "read additional mailmap entries from file"
-msgstr "зчитувати додаткові записи mailmap з файлу"
+msgstr "читати додаткові записи mailmap з файлу"
msgid "blob"
msgstr "blob"
msgid "read additional mailmap entries from blob"
-msgstr "зчитувати додаткові записи mailmap з blob"
+msgstr "читати додаткові записи mailmap з blob"
msgid "no contacts specified"
msgstr "контакти не вказані"
@@ -3538,10 +3546,10 @@ msgid "update stat information in the index file"
msgstr "оновити статистичну інформацію в індексному файлі"
msgid "read list of paths from the standard input"
-msgstr "зчитати список шляхів зі стандартного вводу"
+msgstr "читати список шляхів зі стандартного вводу"
msgid "write the content to temporary files"
-msgstr "записати вміст у тимчасові файли"
+msgstr "записати вміст до тимчасових файлів"
msgid "copy out the files from named stage"
msgstr "скопіювати файли з іменованої стадії"
@@ -4146,11 +4154,94 @@ msgid "clean.requireForce is true and -f not given: refusing to clean"
msgstr ""
"clean.requireForce встановлено у true і -f не задано: відмовлено в прибиранні"
-msgid "git clone [<options>] [--] <repo> [<dir>]"
-msgstr "git clone [<опції>] [--] <сховище> [<директорія>]"
+#, c-format
+msgid "info: Could not add alternate for '%s': %s\n"
+msgstr "інфо: Не вдалося додати запозичений обʼєкт для \"%s\": %s\n"
+
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "не вдалося виконати stat \"%s\""
+
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s існує і не є директорією"
+
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "\"%s\" є символьним посиланням, відмовлено в клонуванні з --local"
+
+#, c-format
+msgid "failed to start iterator over '%s'"
+msgstr "не вдалося запустити ітератор для \"%s\""
+
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr "символьне посилання \"%s\" існує, відмовлено в клонуванні з --local"
+
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "не вдалося видалити \"%s\""
+
+#, c-format
+msgid "hardlink cannot be checked at '%s'"
+msgstr "неможливо перевірити жорстке посилання \"%s\""
+
+#, c-format
+msgid "hardlink different from source at '%s'"
+msgstr "жорстке посилання відрізняється від джерела в \"%s\""
+
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "не вдалося створити посилання \"%s\""
+
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "не вдалося скопіювати файл у \"%s\""
+
+#, c-format
+msgid "failed to iterate over '%s'"
+msgstr "не вдалося здійснити перебір для \"%s\""
+
+#, c-format
+msgid "done.\n"
+msgstr "готово.\n"
+
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry with 'git restore --source=HEAD :/'\n"
+msgstr ""
+"Клонування пройшло успішно, але не вдалося перейти на гілку.\n"
+"Ви можете перевірити, що було додано за допомогою 'git status'\n"
+"і повторити спробу за допомогою \"git restore --source=HEAD :/\"\n"
+
+msgid "remote did not send all necessary objects"
+msgstr "віддалене сховище не надіслало всі необхідні обʼєкти"
+
+#, c-format
+msgid "unable to update %s"
+msgstr "не вдалося оновити %s"
+
+msgid "failed to initialize sparse-checkout"
+msgstr "не вдалося ініціалізувати розріджений перехід"
+
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
+msgstr "віддалений HEAD вказує на неіснуюче посилання, неможливо перейти"
+
+msgid "unable to checkout working tree"
+msgstr "не вдалося завантажити стан робочої директорії"
+
+msgid "unable to write parameters to config file"
+msgstr "не вдалося записати параметри до конфігураційного файлу"
+
+msgid "cannot repack to clean up"
+msgstr "неможливо перепакувати для очищення"
+
+msgid "cannot unlink temporary alternates file"
+msgstr "неможливо видалити тимчасовий файл запозичених обʼєктів"
msgid "don't clone shallow repository"
-msgstr "не клонувати неглибоке сховище"
+msgstr "не клонувати поверхневе сховище"
msgid "don't create a checkout"
msgstr "не переходити на гілку"
@@ -4202,6 +4293,9 @@ msgstr ""
msgid "checkout <branch> instead of the remote's HEAD"
msgstr "перейти до <гілки> замість HEAD віддаленого сховища"
+msgid "clone single revision <rev> and check out"
+msgstr "клонувати одну ревізію <rev> і перейти на неї"
+
msgid "path to git-upload-pack on the remote"
msgstr "шлях до git-upload-pack на віддаленому сервері"
@@ -4223,9 +4317,8 @@ msgstr "поглибити історію неглибокого клону, з�
msgid "clone only one branch, HEAD or --branch"
msgstr "клонувати лише одну гілку, HEAD або --branch"
-msgid "don't clone any tags, and make later fetches not to follow them"
-msgstr ""
-"не клонувати жодних тегів і не слідувати за ними під час отримувань пізніше"
+msgid "clone tags, and make later fetches not to follow them"
+msgstr "клонувати теги і більше не слідкувати за ними"
msgid "any cloned submodules will be shallow"
msgstr "будь-які клоновані підмодулі будуть неглибокими"
@@ -4269,95 +4362,8 @@ msgstr "uri"
msgid "a URI for downloading bundles before fetching from origin remote"
msgstr "URI для завантаження пакунків перед отриманням з віддаленого джерела"
-#, c-format
-msgid "info: Could not add alternate for '%s': %s\n"
-msgstr "инфо: Не вдалося додати запозичений обʼєкт для \"%s\": %s\n"
-
-#, c-format
-msgid "failed to stat '%s'"
-msgstr "не вдалося виконати stat \"%s\""
-
-#, c-format
-msgid "%s exists and is not a directory"
-msgstr "%s існує і не є директорією"
-
-#, c-format
-msgid "'%s' is a symlink, refusing to clone with --local"
-msgstr "\"%s\" є символьним посиланням, відмовлено в клонуванні з --local"
-
-#, c-format
-msgid "failed to start iterator over '%s'"
-msgstr "не вдалося запустити перебір для \"%s\""
-
-#, c-format
-msgid "symlink '%s' exists, refusing to clone with --local"
-msgstr "символьне посилання \"%s\" існує, не можу клонувати з --local"
-
-#, c-format
-msgid "failed to unlink '%s'"
-msgstr "не вдалося видалити \"%s\""
-
-#, c-format
-msgid "hardlink cannot be checked at '%s'"
-msgstr "неможливо перевірити жорстке посилання \"%s\""
-
-#, c-format
-msgid "hardlink different from source at '%s'"
-msgstr "жорстке посилання відрізняється від джерела в \"%s\""
-
-#, c-format
-msgid "failed to create link '%s'"
-msgstr "не вдалося створити посилання \"%s\""
-
-#, c-format
-msgid "failed to copy file to '%s'"
-msgstr "не вдалося скопіювати файл у \"%s\""
-
-#, c-format
-msgid "failed to iterate over '%s'"
-msgstr "не вдалося перебрати \"%s\""
-
-#, c-format
-msgid "done.\n"
-msgstr "готово.\n"
-
-msgid ""
-"Clone succeeded, but checkout failed.\n"
-"You can inspect what was checked out with 'git status'\n"
-"and retry with 'git restore --source=HEAD :/'\n"
-msgstr ""
-"Клонування пройшло успішно, але не вдалося перейти на гілку.\n"
-"Ви можете перевірити, що було додано за допомогою 'git status'\n"
-"і повторити спробу за допомогою 'git restore --source=HEAD :/'\n"
-
-#, c-format
-msgid "Could not find remote branch %s to clone."
-msgstr "Не вдалося знайти віддалену гілку %s для клонування."
-
-msgid "remote did not send all necessary objects"
-msgstr "віддалене сховище не надіслало всі необхідні обʼєкти"
-
-#, c-format
-msgid "unable to update %s"
-msgstr "не вдалося оновити %s"
-
-msgid "failed to initialize sparse-checkout"
-msgstr "не вдалося ініціалізувати розріджений перехід"
-
-msgid "remote HEAD refers to nonexistent ref, unable to checkout"
-msgstr "віддалений HEAD посилається на неіснуючого рефа, неможливо перейти"
-
-msgid "unable to checkout working tree"
-msgstr "не вдалося завантажити стан робочої директорії"
-
-msgid "unable to write parameters to config file"
-msgstr "не вдалося записати параметри до конфігураційного файлу"
-
-msgid "cannot repack to clean up"
-msgstr "неможливо перепакувати, щоб очистити"
-
-msgid "cannot unlink temporary alternates file"
-msgstr "неможливо видалити тимчасовий файл запозичених обʼєктів"
+msgid "git clone [<options>] [--] <repo> [<dir>]"
+msgstr "git clone [<опції>] [--] <сховище> [<директорія>]"
msgid "Too many arguments."
msgstr "Забагато аргументів."
@@ -4463,6 +4469,10 @@ msgstr "операція віддаленого отримання повідо�
msgid "Remote branch %s not found in upstream %s"
msgstr "Віддалену гілку %s не знайдено у першоджерельному сховищі %s"
+#, c-format
+msgid "Remote revision %s not found in upstream %s"
+msgstr "Віддалена ревізія %s не знайдена у першоджерельному сховищі %s"
+
msgid "You appear to have cloned an empty repository."
msgstr "Здається, ви клонували порожнє сховище."
@@ -4639,7 +4649,7 @@ msgid "git commit-tree: failed to read"
msgstr "git commit-tree: не вдалося прочитати"
msgid ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
"reword):]<commit>]\n"
" [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
@@ -4649,7 +4659,7 @@ msgid ""
" [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
" [--] [<pathspec>...]"
msgstr ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<режим>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<режим>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <коміт> | --fixup [(amend|"
"reword):]<коміт>]\n"
" [-F <файл> | -m <допис>] [--reset-author] [--allow-empty]\n"
@@ -5214,7 +5224,7 @@ msgid "blob-id"
msgstr "blob-id"
msgid "read config from given blob object"
-msgstr "прочитати конфігурацію з наданого blob-обʼєкту"
+msgstr "читати конфігурацію з наданого blob-обʼєкту"
msgid "Type"
msgstr "Тип"
@@ -6050,15 +6060,15 @@ msgid ""
"Run 'git remote set-head %s %s' to follow the change, or set\n"
"'remote.%s.followRemoteHEAD' configuration option to a different value\n"
"if you do not want to see this message. Specifically running\n"
-"'git config set remote.%s.followRemoteHEAD %s' will disable the warning\n"
-"until the remote changes HEAD to something else."
+"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n"
+"will disable the warning until the remote changes HEAD to something else."
msgstr ""
"Запустіть \"git remote set-head %s %s\", щоб відстежити зміни, або "
"встановіть\n"
"\"remote.%s.followRemoteHEAD\" параметр конфігурації на інше значення\n"
-"якщо ви не хочете бачити це повідомлення. Зокрема, виконання команди\n"
-"\"git config set remote.%s.followRemoteHEAD %s\" вимкне попередження\n"
-"доки віддалений сервер не змінить HEAD на щось інше."
+"якщо ви не хочете бачити це повідомлення. Зокрема, виконання\n"
+"\"git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s\"\n"
+"вимкне попередження, доки віддалене призначення не змінить HEAD на щось інше."
msgid "multiple branches detected, incompatible with --set-upstream"
msgstr "виявлено кілька гілок, несумісних з --set-upstream"
@@ -6740,6 +6750,9 @@ msgstr "примусово запускати збирач сміття, нав�
msgid "repack all other packs except the largest pack"
msgstr "перепакувати всі пакунки, крім найбільшого"
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr "префікс для зберігання пакунка з обрізаними обʼєктами"
+
#, c-format
msgid "failed to parse gc.logExpiry value %s"
msgstr "не вдалося розібрати gc.logExpiry значення %s"
@@ -7120,7 +7133,7 @@ msgid "show the surrounding function"
msgstr "показати навколишню функцію"
msgid "read patterns from file"
-msgstr "зчитувати шаблони з файлу"
+msgstr "читати шаблони з файлу"
msgid "match <pattern>"
msgstr "зіставляти <шаблон>"
@@ -7196,7 +7209,7 @@ msgid "write the object into the object database"
msgstr "записати об’єкт до бази даних об’єктів"
msgid "read the object from stdin"
-msgstr "прочитати об’єкт з stdin"
+msgstr "читати об’єкт з stdin"
msgid "store file as is without filters"
msgstr "зберегти файл як є без фільтрів"
@@ -7548,6 +7561,10 @@ msgid "Cannot come back to cwd"
msgstr "Неможливо повернутися до поточної робочої директорії"
#, c-format
+msgid "bad --pack_header: %s"
+msgstr "невірний --pack_header: %s"
+
+#, c-format
msgid "bad %s"
msgstr "невірний %s"
@@ -8420,10 +8437,6 @@ msgstr "невідомий варіант стратегії: -X%s"
msgid "malformed input line: '%s'."
msgstr "невірно сформований рядок вводу: \"%s\"."
-#, c-format
-msgid "merging cannot continue; got unclean result of %d"
-msgstr "неможливо продовжити злиття; отримано брудний результат для %d"
-
msgid "git merge [<options>] [<commit>...]"
msgstr "git merge [<опції>] [<коміт>...]"
@@ -9094,7 +9107,7 @@ msgid "Removing note for object %s\n"
msgstr "Видалення нотатки для обʼєкта %s\n"
msgid "read objects from stdin"
-msgstr "зчитати обʼєкти з stdin"
+msgstr "читати обʼєкти з stdin"
msgid "load rewriting config for <command> (implies --stdin)"
msgstr ""
@@ -9216,7 +9229,7 @@ msgid "attempt to remove non-existent note is not an error"
msgstr "спроба видалити неіснуючу нотатку не є помилкою"
msgid "read object names from the standard input"
-msgstr "зчитати імена обʼєктів зі стандартного вводу"
+msgstr "читати імена обʼєктів зі стандартного вводу"
msgid "do not remove, show only"
msgstr "не видаляти, тільки показувати"
@@ -9246,6 +9259,13 @@ msgstr ""
"обʼєктів>]"
#, c-format
+msgid "invalid --name-hash-version option: %d"
+msgstr "неприпустима --name-hash-version опція: %d"
+
+msgid "currently, --write-bitmap-index requires --name-hash-version=1"
+msgstr "наразі --write-bitmap-index потребує --name-hash-version=1"
+
+#, c-format
msgid ""
"write_reuse_object: could not locate %s, expected at offset %<PRIuMAX> in "
"pack %s"
@@ -9481,7 +9501,7 @@ msgid "do not create an empty pack output"
msgstr "не створювати вивід порожнього пакунка"
msgid "read revision arguments from standard input"
-msgstr "зчитувати аргументи ревізії зі стандартного вводу"
+msgstr "читати аргументи ревізії зі стандартного вводу"
msgid "limit the objects to those that are not yet packed"
msgstr "обмежувати обʼєкти тільки тими, які ще не запаковані"
@@ -9570,6 +9590,10 @@ msgstr "протокол"
msgid "exclude any configured uploadpack.blobpackfileuri with this protocol"
msgstr "вилучити всі налаштовані uploadpack.blobpackfileuri з цим протоколом"
+msgid "use the specified name-hash function to group similar objects"
+msgstr ""
+"використовувати вказану name-hash функцію для групування схожих обʼєктів"
+
#, c-format
msgid "delta chain depth %d is too deep, forcing %d"
msgstr "глибина дельта ланцюжка %d занадто глибока, примусове %d"
@@ -10826,8 +10850,8 @@ msgstr "не вказано журнал посилань для видален�
msgid "invalid ref format: %s"
msgstr "неприпустимий формат посилання: %s"
-msgid "git refs migrate --ref-format=<format> [--dry-run]"
-msgstr "git refs migrate --ref-format=<формат> [--dry-run]"
+msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
+msgstr "git refs migrate --ref-format=<формат> [--no-reflog] [--dry-run]"
msgid "git refs verify [--strict] [--verbose]"
msgstr "git refs verify [--strict] [--verbose]"
@@ -10838,6 +10862,9 @@ msgstr "вкажіть формат посилання, в який потріб
msgid "perform a non-destructive dry-run"
msgstr "виконати неруйнівний пробний запуск"
+msgid "drop reflogs entirely during the migration"
+msgstr "повністю видалити журнал посилань під час міграції"
+
msgid "missing --ref-format=<format>"
msgstr "відсутній --ref-format=<формат>"
@@ -11312,8 +11339,14 @@ msgstr "Не видалятиме всі URL-адреси, що не є приз
msgid "be verbose; must be placed before a subcommand"
msgstr "розгорнутий вивід; має стояти перед підкомандою"
-msgid "git repack [<options>]"
-msgstr "git repack [<опції>]"
+msgid ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
+msgstr ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<н>] [--depth=<н>] [--threads=<н>] [--keep-pack=<назва-пакунка>]\n"
+"[--write-midx] [--name-hash-version=<н>]"
msgid ""
"Incremental repacks are incompatible with bitmap indexes. Use\n"
@@ -11388,6 +11421,10 @@ msgstr "передати --no-reuse-delta до git-pack-objects"
msgid "pass --no-reuse-object to git-pack-objects"
msgstr "передати --no-reuse-object до git-pack-objects"
+msgid ""
+"specify the name hash version to use for grouping similar objects by path"
+msgstr "вказати версію назви хешу для групування схожих обʼєктів за шляхом"
+
msgid "do not run git-update-server-info"
msgstr "не запускати git-update-server-info"
@@ -11437,9 +11474,6 @@ msgstr "знайти геометричну прогресію з факторо
msgid "write a multi-pack index of the resulting packs"
msgstr "записати multi-pack-index результуючих пакунків"
-msgid "pack prefix to store a pack containing pruned objects"
-msgstr "префікс для зберігання пакунка з обрізаними обʼєктами"
-
msgid "pack prefix to store a pack containing filtered out objects"
msgstr "префікс для зберігання пакунка з відфільтрованими обʼєктами"
@@ -11656,9 +11690,6 @@ msgstr "тільки один шаблон може бути заданий з -
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 "всі надані позитивні ревізії мають бути посиланнями"
@@ -12135,7 +12166,7 @@ msgid "use stateless RPC protocol"
msgstr "використовувати протокол RPC без збереження стану"
msgid "read refs from stdin"
-msgstr "прочитати посилання з stdin"
+msgstr "читати посилання з stdin"
msgid "print status from remote helper"
msgstr "вивести статус з віддаленого помічника"
@@ -13623,7 +13654,7 @@ msgid "with --stdin: input lines are terminated by null bytes"
msgstr "з --stdin: вхідні рядки завершуються нульовими байтами"
msgid "read list of paths to be updated from standard input"
-msgstr "прочитати список шляхів для оновлення зі стандартного вводу"
+msgstr "читати список шляхів для оновлення зі стандартного вводу"
msgid "add entries from standard input to the index"
msgstr "додати записи зі стандартного вводу до індексу"
@@ -14335,8 +14366,11 @@ msgstr "Імпортувати GNU Arch сховище до Git"
msgid "Create an archive of files from a named tree"
msgstr "Створити архів файлів з названого дерева"
+msgid "Download missing objects in a partial clone"
+msgstr "Завантажити відсутні обʼєкти у частковому клонуванні"
+
msgid "Use binary search to find the commit that introduced a bug"
-msgstr "Використати бінарний пошук, щоб знайти коміт, який вніс помилку"
+msgstr "Використати бінарний пошук, щоб знайти коміт, який ввів помилку"
msgid "Show what revision and author last modified each line of a file"
msgstr "Показати, яка ревізія та автор востаннє змінювали кожен рядок файлу"
@@ -15728,7 +15762,7 @@ msgstr "помилка протоколу: неочікувані здібнос
#, c-format
msgid "protocol error: expected shallow sha-1, got '%s'"
-msgstr "помилка протоколу: очікувалось неглибоке sha-1, отримано \"%s\""
+msgstr "помилка протоколу: очікувалось поверхневе sha-1, отримано \"%s\""
msgid "repository on the other end cannot be shallow"
msgstr "сховище на іншому кінці не може бути неглибоким"
@@ -16256,6 +16290,12 @@ msgstr "неприпустимий аргумент до %s"
msgid "invalid regex given to -I: '%s'"
msgstr "неприпустимий regex, переданий до -I: \"%s\""
+msgid "-G requires a non-empty argument"
+msgstr "-G потребує непорожнього аргументу"
+
+msgid "-S requires a non-empty argument"
+msgstr "-S потребує непорожнього аргументу"
+
#, c-format
msgid "failed to parse --submodule option parameter: '%s'"
msgstr "не вдалося розібрати параметр опції --submodule: \"%s\""
@@ -17247,7 +17287,7 @@ msgstr ""
#, c-format
msgid "git: '%s' is not a git command. See 'git --help'."
-msgstr "git: \"%s\" не є командою git. Дивітья git --help."
+msgstr "git: \"%s\" не є командою git. Дивіться git --help."
msgid "Uh oh. Your system reports no Git commands at all."
msgstr "Ой-ой. Ваша система повідомляє про повну відсутність Git команд."
@@ -18448,6 +18488,10 @@ msgid "unable to write file %s"
msgstr "не вдалося записати файл %s"
#, c-format
+msgid "unable to write repeatedly vanishing file %s"
+msgstr "не вдалося записати файл %s, який постійно зникає"
+
+#, c-format
msgid "unable to set permission to '%s'"
msgstr "не вдалося встановити дозволи для \"%s\""
@@ -19020,6 +19064,52 @@ msgstr "невідомий перемикач \"%c\""
msgid "unknown non-ascii option in string: `%s'"
msgstr "невідомий non-ascii параметр у рядку: \"%s\""
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the long form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[=<%s>]"
+msgstr "[=<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the short form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[<%s>]"
+msgstr "[<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string stands for a
+#. value given to a command line option, and "<>" is there
+#. as a convention to signal that it is a placeholder
+#. (i.e. the user should substitute it with the real value).
+#. If your language uses a different convention, you can
+#. change "<%s>" part to match yours, e.g. it might use
+#. "|%s|" instead, or if the alphabet is different enough it
+#. may use "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid " <%s>"
+msgstr " <%s>"
+
msgid "..."
msgstr "..."
@@ -19107,6 +19197,21 @@ msgid "failed to parse %s"
msgstr "не вдалося розібрати %s"
#, c-format
+msgid "failed to walk children of tree %s: not found"
+msgstr "не вдалося пройти дочірні елементи дерева %s: не знайдено"
+
+#, c-format
+msgid "failed to find object %s"
+msgstr "не вдалося знайти обʼєкт %s"
+
+#, c-format
+msgid "failed to find tag %s"
+msgstr "не вдалося знайти тег %s"
+
+msgid "failed to setup revision walk"
+msgstr "не вдалося налаштувати проходження по ревізіям"
+
+#, c-format
msgid "Could not make %s writable by group"
msgstr "Не вдалося зробити %s доступним для запису групою"
@@ -19255,6 +19360,22 @@ msgstr "назва віддаленого promisor не може починат�
msgid "could not fetch %s from promisor remote"
msgstr "не вдалося отримати %s з віддаленого promisor"
+#, c-format
+msgid "known remote named '%s' but with url '%s' instead of '%s'"
+msgstr "відоме віддалене сховище з імʼям \"%s\" має URL \"%s\" замість \"%s\""
+
+#, c-format
+msgid "unknown '%s' value for '%s' config option"
+msgstr "невідоме значення \"%s\" для параметра конфігурації \"%s\""
+
+#, c-format
+msgid "unknown element '%s' from remote info"
+msgstr "невідомий елемент \"%s\" з віддаленої інформації"
+
+#, c-format
+msgid "accepted promisor remote '%s' not found"
+msgstr "прийнятий віддалений promisor \"%s\" не знайдено"
+
msgid "object-info: expected flush after arguments"
msgstr "object-info: очікувався flush після аргументів"
@@ -20084,6 +20205,14 @@ msgid "invalid refspec '%s'"
msgstr "неприпустимий визначник посилання \"%s\""
#, c-format
+msgid "pattern '%s' has no '*'"
+msgstr "шаблон \"%s\" не має \"*\""
+
+#, c-format
+msgid "replacement '%s' has no '*'"
+msgstr "заміна \"%s\" не має \"*\""
+
+#, c-format
msgid "invalid quoting in push-option value: '%s'"
msgstr "неприпустимі лапки у значенні push-опції: \"%s\""
@@ -20132,7 +20261,7 @@ msgstr "віддалений сервер надіслав неочікуван�
msgid "unable to rewind rpc post data - try increasing http.postBuffer"
msgstr ""
-"не вдалося перемотати вперед rpc post дані - спробуйте збільшити "
+"не вдалося перемотати вперед rpc post дані - спробуйте збільшити "
"http.postBuffer"
#, c-format
@@ -20207,8 +20336,31 @@ msgid "remote-curl: unknown command '%s' from git"
msgstr "remote-curl: невідома команда \"%s\" з git"
#, c-format
+msgid ""
+"reading remote from \"%s/%s\", which is nominated for removal.\n"
+"\n"
+"If you still use the \"remotes/\" directory it is recommended to\n"
+"migrate to config-based remotes:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"If you cannot, please let us know why you still need to use it by\n"
+"sending an e-mail to <git@vger.kernel.org>."
+msgstr ""
+"читання віддаленого призначення з \"%s/%s\", який номіновано на вилучення.\n"
+"\n"
+"Якщо ви все ще використовуєте директорію \"remotes/\", рекомендується\n"
+"перейти віддалені призначення на основі конфігурації:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"Якщо ви не можете цього зробити, будь ласка, дайте нам знати, чому ви все ще "
+"використовуєте її\n"
+"надіславши листа на адресу <git@vger.kernel.org>."
+
+#, c-format
msgid "config remote shorthand cannot begin with '/': %s"
-msgstr "скорочення віддаленої конфігураціі не може починатися з \"/\": %s"
+msgstr "скорочене ім'я віддаленої конфігураціі не може починатися з \"/\": %s"
msgid "more than one receivepack given, using the first"
msgstr "надано більше одного пакунка для отримання, використано перший"
@@ -20241,14 +20393,6 @@ msgid "%s tracks both %s and %s"
msgstr "%s відстежує як %s, так і %s"
#, c-format
-msgid "key '%s' of pattern had no '*'"
-msgstr "ключ \"%s\" шаблону не містив '*'"
-
-#, c-format
-msgid "value '%s' of pattern has no '*'"
-msgstr "значення \"%s\" шаблону не містить '*'"
-
-#, c-format
msgid "src refspec %s does not match any"
msgstr "визначник посилання джерела %s не збігається з жодним"
@@ -22179,6 +22323,27 @@ msgid "number of entries in the cache tree to invalidate (default 0)"
msgstr ""
"кількість записів у дереві кешу, які потрібно анулювати (за замовчуванням 0)"
+msgid "test-tool path-walk <options> -- <revision-options>"
+msgstr "test-tool path-walk <опції> -- <опції-ревізії>."
+
+msgid "toggle inclusion of blob objects"
+msgstr "перемикач включення обʼєктів blob"
+
+msgid "toggle inclusion of commit objects"
+msgstr "перемикач включення обʼєктів коміту"
+
+msgid "toggle inclusion of tag objects"
+msgstr "перемикач включення обʼєктів тегів"
+
+msgid "toggle inclusion of tree objects"
+msgstr "перемикач включення обʼєктів дерева"
+
+msgid "toggle pruning of uninteresting paths"
+msgstr "перемикач обрізання нецікавих шляхів"
+
+msgid "read a pattern list over stdin"
+msgstr "читати список шаблонів через stdin"
+
#, c-format
msgid "commit %s is not marked reachable"
msgstr "коміт %s не позначений як досяжний"
@@ -22819,6 +22984,10 @@ msgstr "помилка: "
msgid "warning: "
msgstr "попередження: "
+#, c-format
+msgid "uname() failed with error '%s' (%d)\n"
+msgstr "uname() завершився невдало з помилкою \"%s\" (%d)\n"
+
msgid "Fetching objects"
msgstr "Отримання обʼєктів"
diff --git a/po/vi.po b/po/vi.po
index 80a5f191a8..803a68e1b6 100644
--- a/po/vi.po
+++ b/po/vi.po
@@ -11,7 +11,7 @@
# Vũ Tiến Hưng <newcomerminecraft@gmail.com>, 2024-2025.
# ---
# BẢNG THUẬT NGỮ / TERMINOLOGY
-# Updated: 2024-07-26, git 2.46
+# Updated: 2025-03-06, git 2.49
#
# Ghi chú:
# - Bảng thuật ngữ này chưa hoàn thiện.
@@ -59,15 +59,17 @@
# | (v.) rebase | cải tổ |
# | (v.) squash | squash |
# | (v.) amend | tu bổ |
+# | (n.) revision | cải biên |
+# | (n.) repo/repository | kho chứa |
# | | |
# | ... TODO ... | |
# +------------------------------------------------------------------+
msgid ""
msgstr ""
-"Project-Id-Version: git 2.48\n"
+"Project-Id-Version: git 2.49\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2024-12-23 18:57+0000\n"
-"PO-Revision-Date: 2025-01-05 01:20+0700\n"
+"POT-Creation-Date: 2025-03-05 22:57+0000\n"
+"PO-Revision-Date: 2025-03-06 08:20+0700\n"
"Last-Translator: Vũ Tiến Hưng <newcomerminecraft@gmail.com>\n"
"Language-Team: Vietnamese <https://github.com/Nekosha/git-po>\n"
"Language: vi\n"
@@ -1601,7 +1603,7 @@ msgid ""
"git bisect cannot work properly in this case.\n"
"Maybe you mistook %s and %s revs?\n"
msgstr ""
-"Một số điểm xét duyệt %s không phải tổ tiên của điểm xét duyệt %s.\n"
+"Một số lần cải biên %s không phải tổ tiên của lần cải biên %s.\n"
"git bisect không thể làm việc đúng trong trường hợp này.\n"
"Liệu có phải bạn nhầm lẫn các điểm %s và %s không?\n"
@@ -1621,7 +1623,7 @@ msgstr "Đang bisect: gốc hòa trộn cần phải được kiểm tra\n"
#, c-format
msgid "a %s revision is needed"
-msgstr "cần một điểm xét duyệt %s"
+msgstr "cần lần cải biên %s"
#, c-format
msgid "could not create file '%s'"
@@ -1661,7 +1663,7 @@ msgstr[0] "(cần khoảng chừng %d bước)"
#, c-format
msgid "Bisecting: %d revision left to test after this %s\n"
msgid_plural "Bisecting: %d revisions left to test after this %s\n"
-msgstr[0] "Bisecting: còn %d điểm xét duyệt để kiểm tra %s\n"
+msgstr[0] "Bisecting: còn %d lần cải biên để kiểm tra %s\n"
msgid "--contents and --reverse do not blend well."
msgstr "tùy chọn --contents và --reverse không nên đi với nhau."
@@ -1671,7 +1673,7 @@ msgstr ""
"cùng sử dụng --reverse và --first-parent cần chỉ định lần chuyển giao cuối"
msgid "revision walk setup failed"
-msgstr "cài đặt việc di chuyển qua các điểm xét duyệt gặp lỗi"
+msgstr "cài đặt việc duyệt qua các lần cải biên gặp lỗi"
msgid ""
"--reverse --first-parent together require range along first-parent chain"
@@ -2371,6 +2373,18 @@ msgstr "git archive: lỗi giao thức"
msgid "git archive: expected a flush"
msgstr "git archive: cần flush dữ liệu"
+msgid "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+msgstr "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+
+msgid "problem loading sparse-checkout"
+msgstr "không thể tải checkout thưa"
+
+msgid "Minimum number of objects to request at a time"
+msgstr "Số đối tượng tối thiểu mỗi lần yêu cầu"
+
+msgid "Restrict the missing objects to the current sparse-checkout"
+msgstr "Chỉ lấy về đối tượng còn thiếu trong checkout thưa hiện tại"
+
msgid ""
"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>] [--no-"
"checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]"
@@ -2441,7 +2455,7 @@ msgstr "Đối số bisect_write sai: %s"
#, c-format
msgid "couldn't get the oid of the rev '%s'"
-msgstr "không thể lấy oid của điểm xét duyệt '%s'"
+msgstr "không thể lấy oid của lần cải biên '%s'"
#, c-format
msgid "couldn't open the file '%s'"
@@ -2466,7 +2480,7 @@ msgid ""
"You can use \"git bisect %s\" and \"git bisect %s\" for that."
msgstr ""
"Bạn cần bắt đầu bằng lệnh \"git bisect start\".\n"
-"Bạn sau đó cần phải chỉ cho tôi ít nhất một điểm xét duyệt %s và một %s.\n"
+"Bạn sau đó cần phải chỉ cho tôi ít nhất một lần cải biên %s và một %s.\n"
"Bạn có thể sử dụng \"git bisect %s\" và \"git bisect %s\" cho chúng."
#, c-format
@@ -2526,7 +2540,7 @@ msgstr "không nhận ra tuỳ chọn: '%s'"
#, c-format
msgid "'%s' does not appear to be a valid revision"
-msgstr "'%s' không có vẻ như là một điểm xét duyệt hợp lệ"
+msgstr "'%s' không có vẻ như là một lần cải biên hợp lệ"
msgid "bad HEAD - I need a HEAD"
msgstr "sai HEAD - Tôi cần một HEAD"
@@ -2587,11 +2601,11 @@ msgstr "bisect chạy gặp lỗi: không đưa ra lệnh."
#, c-format
msgid "unable to verify %s on good revision"
-msgstr "không thể xác nhận '%s' trên điểm xét duyệt tốt"
+msgstr "không thể xác nhận '%s' trên lần cải biên tốt"
#, c-format
msgid "bogus exit code %d for good revision"
-msgstr "mã trả về %d bất thường cho điểm xét duyệt tốt"
+msgstr "mã trả về %d bất thường cho lần cải biên tốt"
#, c-format
msgid "bisect run failed: exit code %d from %s is < 0 or >= 128"
@@ -2658,7 +2672,7 @@ msgstr "phải kết thúc bằng một màu"
#, c-format
msgid "cannot find revision %s to ignore"
-msgstr "không thể tìm thấy điểm xét duyệt %s để bỏ qua"
+msgstr "không thể tìm thấy lần cải biên %s để bỏ qua"
msgid "show blame entries as we find them, incrementally"
msgstr "hiển thị các mục 'blame' theo thời gian, tăng dần"
@@ -2717,7 +2731,7 @@ msgid "ignore <rev> when blaming"
msgstr "bỏ qua <rev> khi blame"
msgid "ignore revisions from <file>"
-msgstr "bỏ qua các điểm xét duyệt từ <tập tin>"
+msgstr "bỏ qua các lần cải biên từ <tập tin>"
msgid "color redundant metadata from previous line differently"
msgstr "tô màu khác cho siêu dữ liệu dư thừa từ dòng trước"
@@ -2730,7 +2744,7 @@ msgstr "tiêu thụ thêm tài nguyên để tìm kiếm tốt hơn nữa"
msgid "use revisions from <file> instead of calling git-rev-list"
msgstr ""
-"sử dụng các điểm xét duyệt (revision) từ <tập tin> thay vì gọi git-rev-list"
+"sử dụng các lần cải biên (revision) từ <tập tin> thay vì gọi git-rev-list"
msgid "use <file>'s contents as the final image"
msgstr "sử dụng nội dung của <tập tin> như là ảnh cuối cùng"
@@ -3114,10 +3128,6 @@ msgstr ""
msgid "git version:\n"
msgstr "phiên bản git:\n"
-#, c-format
-msgid "uname() failed with error '%s' (%d)\n"
-msgstr "uname() gặp lỗi '%s' (%d)\n"
-
msgid "compiler info: "
msgstr "thông tin trình biên dịch: "
@@ -3710,7 +3720,7 @@ msgstr[0] ""
"\n"
msgid "internal error in revision walk"
-msgstr "lỗi nội bộ trong khi di chuyển qua các điểm xét duyệt"
+msgstr "lỗi nội bộ trong khi di chuyển qua các lần cải biên"
msgid "Previous HEAD position was"
msgstr "Vị trí trước kia của HEAD là"
@@ -4116,8 +4126,91 @@ msgstr ""
"clean.requireForce được đặt thành true và không có tuỳ chọn -f; từ chối dọn "
"dẹp"
-msgid "git clone [<options>] [--] <repo> [<dir>]"
-msgstr "git clone [<các tùy chọn>] [--] <kho> [<t.mục>]"
+#, c-format
+msgid "info: Could not add alternate for '%s': %s\n"
+msgstr "thông tin: không thể thêm thay thế cho '%s': %s\n"
+
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "gặp lỗi khi stat '%s'"
+
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s có tồn tại nhưng lại không phải là một thư mục"
+
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "'%s' là liên kết mềm, từ chối sao chép với --local"
+
+#, c-format
+msgid "failed to start iterator over '%s'"
+msgstr "gặp lỗi khi bắt đầu lặp qua '%s'"
+
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr "liên kết mềm '%s' đã tồn tại, từ chối sao chép với --local"
+
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "gặp lỗi khi unlink '%s'"
+
+#, c-format
+msgid "hardlink cannot be checked at '%s'"
+msgstr "không thể kiểm tra liên kết cứng '%s'"
+
+#, c-format
+msgid "hardlink different from source at '%s'"
+msgstr "liên kết cứng '%s' khác với nguồn"
+
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "gặp lỗi khi tạo liên kết mềm %s"
+
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "gặp lỗi khi sao chép tập tin tới '%s'"
+
+#, c-format
+msgid "failed to iterate over '%s'"
+msgstr "gặp lỗi khi lặp qua '%s'"
+
+#, c-format
+msgid "done.\n"
+msgstr "hoàn tất.\n"
+
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry with 'git restore --source=HEAD :/'\n"
+msgstr ""
+"Việc nhân bản thành công, nhưng checkout gặp lỗi.\n"
+"Kiểm tra xem cái gì đã được checkout bằng lệnh 'git status'\n"
+"và thử lại với lệnh 'git restore --source=HEAD :/'\n"
+
+msgid "remote did not send all necessary objects"
+msgstr "máy chủ đã không gửi tất cả các đối tượng cần thiết"
+
+#, c-format
+msgid "unable to update %s"
+msgstr "không thể cập nhật %s"
+
+msgid "failed to initialize sparse-checkout"
+msgstr "gặp lỗi khi khởi tạo sparse-checkout"
+
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
+msgstr "HEAD ở máy chủ chỉ đến ref không tồn tại, không thể checkout"
+
+msgid "unable to checkout working tree"
+msgstr "không thể checkout cây làm việc"
+
+msgid "unable to write parameters to config file"
+msgstr "không thể ghi các tham số vào tập tin cấu hình"
+
+msgid "cannot repack to clean up"
+msgstr "không thể đóng gói để dọn dẹp"
+
+msgid "cannot unlink temporary alternates file"
+msgstr "không thể bỏ liên kết tập tin thay thế tạm thời"
msgid "don't clone shallow repository"
msgstr "đừng nhân bản từ kho nông"
@@ -4170,6 +4263,9 @@ msgstr "dùng <tên> thay cho 'origin' để theo dõi thượng nguồn"
msgid "checkout <branch> instead of the remote's HEAD"
msgstr "checkout <nhánh> thay cho HEAD của máy chủ"
+msgid "clone single revision <rev> and check out"
+msgstr "nhân bản chỉ lần cải biên <rev> and check out"
+
msgid "path to git-upload-pack on the remote"
msgstr "đường dẫn đến git-upload-pack trên máy chủ"
@@ -4191,10 +4287,8 @@ msgstr "làm sâu hơn lịch sử của bản sao shallow, loại trừ tham ch
msgid "clone only one branch, HEAD or --branch"
msgstr "chỉ nhân bản một nhánh, HEAD hoặc --branch"
-msgid "don't clone any tags, and make later fetches not to follow them"
-msgstr ""
-"đứng có nhân bản bất kỳ nhánh nào, và làm cho những lần lấy về sau không "
-"theo chúng nữa"
+msgid "clone tags, and make later fetches not to follow them"
+msgstr "nhân bản thẻ, và làm cho những lần lấy về sau không theo chúng nữa"
msgid "any cloned submodules will be shallow"
msgstr "mọi mô-đun-con nhân bản sẽ là shallow (nông)"
@@ -4235,95 +4329,8 @@ msgstr "uri"
msgid "a URI for downloading bundles before fetching from origin remote"
msgstr "URI để tải xuống bundle trước khi lấy về từ máy chủ origin"
-#, c-format
-msgid "info: Could not add alternate for '%s': %s\n"
-msgstr "thông tin: không thể thêm thay thế cho '%s': %s\n"
-
-#, c-format
-msgid "failed to stat '%s'"
-msgstr "gặp lỗi khi stat '%s'"
-
-#, c-format
-msgid "%s exists and is not a directory"
-msgstr "%s có tồn tại nhưng lại không phải là một thư mục"
-
-#, c-format
-msgid "'%s' is a symlink, refusing to clone with --local"
-msgstr "'%s' là liên kết mềm, từ chối sao chép với --local"
-
-#, c-format
-msgid "failed to start iterator over '%s'"
-msgstr "gặp lỗi khi bắt đầu lặp qua '%s'"
-
-#, c-format
-msgid "symlink '%s' exists, refusing to clone with --local"
-msgstr "liên kết mềm '%s' đã tồn tại, từ chối sao chép với --local"
-
-#, c-format
-msgid "failed to unlink '%s'"
-msgstr "gặp lỗi khi unlink '%s'"
-
-#, c-format
-msgid "hardlink cannot be checked at '%s'"
-msgstr "không thể kiểm tra liên kết cứng '%s'"
-
-#, c-format
-msgid "hardlink different from source at '%s'"
-msgstr "liên kết cứng '%s' khác với nguồn"
-
-#, c-format
-msgid "failed to create link '%s'"
-msgstr "gặp lỗi khi tạo liên kết mềm %s"
-
-#, c-format
-msgid "failed to copy file to '%s'"
-msgstr "gặp lỗi khi sao chép tập tin tới '%s'"
-
-#, c-format
-msgid "failed to iterate over '%s'"
-msgstr "gặp lỗi khi lặp qua '%s'"
-
-#, c-format
-msgid "done.\n"
-msgstr "hoàn tất.\n"
-
-msgid ""
-"Clone succeeded, but checkout failed.\n"
-"You can inspect what was checked out with 'git status'\n"
-"and retry with 'git restore --source=HEAD :/'\n"
-msgstr ""
-"Việc nhân bản thành công, nhưng checkout gặp lỗi.\n"
-"Kiểm tra xem cái gì đã được checkout bằng lệnh 'git status'\n"
-"và thử lại với lệnh 'git restore --source=HEAD :/'\n"
-
-#, c-format
-msgid "Could not find remote branch %s to clone."
-msgstr "Không tìm thấy nhánh máy chủ %s để nhân bản (clone)."
-
-msgid "remote did not send all necessary objects"
-msgstr "máy chủ đã không gửi tất cả các đối tượng cần thiết"
-
-#, c-format
-msgid "unable to update %s"
-msgstr "không thể cập nhật %s"
-
-msgid "failed to initialize sparse-checkout"
-msgstr "gặp lỗi khi khởi tạo sparse-checkout"
-
-msgid "remote HEAD refers to nonexistent ref, unable to checkout"
-msgstr "HEAD ở máy chủ chỉ đến ref không tồn tại, không thể checkout"
-
-msgid "unable to checkout working tree"
-msgstr "không thể checkout cây làm việc"
-
-msgid "unable to write parameters to config file"
-msgstr "không thể ghi các tham số vào tập tin cấu hình"
-
-msgid "cannot repack to clean up"
-msgstr "không thể đóng gói để dọn dẹp"
-
-msgid "cannot unlink temporary alternates file"
-msgstr "không thể bỏ liên kết tập tin thay thế tạm thời"
+msgid "git clone [<options>] [--] <repo> [<dir>]"
+msgstr "git clone [<các tùy chọn>] [--] <kho> [<t.mục>]"
msgid "Too many arguments."
msgstr "Quá nhiều đối số."
@@ -4430,6 +4437,10 @@ msgstr "trình vận chuyển đã báo lỗi"
msgid "Remote branch %s not found in upstream %s"
msgstr "Nhánh máy chủ %s không tìm thấy trong thượng nguồn %s"
+#, c-format
+msgid "Remote revision %s not found in upstream %s"
+msgstr "Lần cải biên %s không tìm thấy trong thượng nguồn %s"
+
msgid "You appear to have cloned an empty repository."
msgstr "Bạn hình như là đã nhân bản một kho trống rỗng."
@@ -4605,7 +4616,7 @@ msgid "git commit-tree: failed to read"
msgstr "git commit-tree: gặp lỗi khi đọc"
msgid ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
"reword):]<commit>]\n"
" [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
@@ -5576,7 +5587,7 @@ msgstr ""
#, c-format
msgid "traversed %lu commits\n"
-msgstr "đã xuyên %lu qua lần chuyển giao\n"
+msgstr "đã chạy qua %lu lần chuyển giao\n"
#, c-format
msgid "found %i tags; gave up search at %s\n"
@@ -6021,8 +6032,8 @@ msgid ""
"Run 'git remote set-head %s %s' to follow the change, or set\n"
"'remote.%s.followRemoteHEAD' configuration option to a different value\n"
"if you do not want to see this message. Specifically running\n"
-"'git config set remote.%s.followRemoteHEAD %s' will disable the warning\n"
-"until the remote changes HEAD to something else."
+"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n"
+"will disable the warning until the remote changes HEAD to something else."
msgstr ""
"Chạy 'git remote set-head %s %s' để làm theo thay đổi, hoặc đặt tuỳ chọn\n"
"'remote.%s.followRemoteHEAD' sang giá trị khác nếu bạn không muốn thấy\n"
@@ -6073,7 +6084,7 @@ msgid ""
"remote name from which new revisions should be fetched"
msgstr ""
"chưa chỉ ra kho chứa máy chủ; xin hãy chỉ định hoặc là URL hoặc\n"
-"tên máy chủ từ cái mà những điểm xét duyệt mới có thể được fetch (lấy về)"
+"tên máy chủ từ cái mà những lần cải biên mới có thể được fetch (lấy về)"
msgid "you need to specify a tag name"
msgstr "bạn cần chỉ định một tên thẻ"
@@ -6167,7 +6178,7 @@ msgid "specify fetch refmap"
msgstr "chỉ ra refmap cần lấy về"
msgid "revision"
-msgstr "điểm xét duyệt"
+msgstr "lần cải biên"
msgid "report that we have only objects reachable from this object"
msgstr "báo rằng ta chỉ có các đối tượng tiếp cận được từ đối tượng này"
@@ -6705,6 +6716,9 @@ msgstr "buộc gc chạy ngay cả khi có tiến trình gc khác đang chạy"
msgid "repack all other packs except the largest pack"
msgstr "đóng gói lại tất cả các gói khác ngoại trừ gói lớn nhất"
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr "tiền tố của gói để lưu gói gồm những đối tượng đã loại bỏ"
+
#, c-format
msgid "failed to parse gc.logExpiry value %s"
msgstr "gặp lỗi khi đọc giá trị gc.logExpiry %s"
@@ -7113,7 +7127,7 @@ msgstr "--no-index hay --untracked không được sử dụng cùng với revs"
#, c-format
msgid "unable to resolve revision: %s"
-msgstr "không thể phân giải điểm xét duyệt: %s"
+msgstr "không thể phân giải lần cải biên: %s"
msgid "--untracked not supported with --recurse-submodules"
msgstr "tùy chọn --untracked không được hỗ trợ với --recurse-submodules"
@@ -7491,6 +7505,10 @@ msgid "Cannot come back to cwd"
msgstr "Không thể quay lại thư mục hiện hành"
#, c-format
+msgid "bad --pack_header: %s"
+msgstr "--pack_header sai: %s"
+
+#, c-format
msgid "bad %s"
msgstr "%s sai"
@@ -7758,10 +7776,10 @@ msgid "failed to find exact merge base"
msgstr "gặp lỗi khi tìm gốc hòa trộn chính xác"
msgid "base commit should be the ancestor of revision list"
-msgstr "lần chuyển giao nền không là tổ tiên của danh sách điểm xét duyệt"
+msgstr "lần chuyển giao nền không là tổ tiên của danh sách lần cải biên"
msgid "base commit shouldn't be in revision list"
-msgstr "lần chuyển giao nền không được trong danh sách điểm xét duyệt"
+msgstr "lần chuyển giao nền không được trong danh sách lần cải biên"
msgid "cannot get patch id"
msgstr "không thể lấy mã bản vá"
@@ -8356,10 +8374,6 @@ msgstr "không hiểu chiến lược: -X%s"
msgid "malformed input line: '%s'."
msgstr "dòng đầu vào sai quy cách: '%s'."
-#, c-format
-msgid "merging cannot continue; got unclean result of %d"
-msgstr "không thể tiếp tục hoà trộn; kết quả không hoàn toàn %d"
-
msgid "git merge [<options>] [<commit>...]"
msgstr "git merge [<các tùy chọn>] [<commit>...]"
@@ -9187,6 +9201,13 @@ msgstr ""
"sách-đối-tượng>]"
#, c-format
+msgid "invalid --name-hash-version option: %d"
+msgstr "tùy chọn --name-hash-version không hợp lệ: %d"
+
+msgid "currently, --write-bitmap-index requires --name-hash-version=1"
+msgstr "--write-bitmap-index hiện cần --name-hash-version=1"
+
+#, c-format
msgid ""
"write_reuse_object: could not locate %s, expected at offset %<PRIuMAX> in "
"pack %s"
@@ -9363,7 +9384,7 @@ msgstr "không phải một rev '%s'"
#, c-format
msgid "bad revision '%s'"
-msgstr "điểm xem xét sai '%s'"
+msgstr "lần cải biên sai '%s'"
msgid "unable to add recent objects"
msgstr "không thể thêm các đối tượng mới dùng"
@@ -9509,6 +9530,9 @@ msgstr "giao thức"
msgid "exclude any configured uploadpack.blobpackfileuri with this protocol"
msgstr "loại trừ bất kỳ cấu hình uploadpack.blobpackfileuri với giao thức này"
+msgid "use the specified name-hash function to group similar objects"
+msgstr "dùng hàm băm này để nhóm các đối tượng giống nhau"
+
#, c-format
msgid "delta chain depth %d is too deep, forcing %d"
msgstr "mức delta chain %d là quá sâu, buộc dùng %d"
@@ -10261,7 +10285,7 @@ msgid ""
msgstr ""
"\n"
"git gặp phải một lỗi trong khi đang chuẩn bị các bản vá để diễn lại\n"
-"những điểm xét duyệt này:\n"
+"những lần cải biên này:\n"
"\n"
" %s\n"
"\n"
@@ -10750,8 +10774,8 @@ msgstr "chưa chỉ ra reflog để xóa"
msgid "invalid ref format: %s"
msgstr "định dạng tham chiếu không hợp lệ: %s"
-msgid "git refs migrate --ref-format=<format> [--dry-run]"
-msgstr "git refs migrate --ref-format=<định dạng> [--dry-run]"
+msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
+msgstr "git refs migrate --ref-format=<định dạng> [--no-reflog] [--dry-run]"
msgid "git refs verify [--strict] [--verbose]"
msgstr "git refs verify [--strict] [--verbose]"
@@ -10762,6 +10786,9 @@ msgstr "chỉ định định dạng tham chiếu để chuyển đổi sang"
msgid "perform a non-destructive dry-run"
msgstr "chạy thử mà không thay đổi gì"
+msgid "drop reflogs entirely during the migration"
+msgstr "bỏ reflog khi chuyển đổi"
+
msgid "missing --ref-format=<format>"
msgstr "thiếu --ref-format=<định dạng>"
@@ -11216,8 +11243,14 @@ msgstr "Sẽ không xóa những địa chỉ URL không-push"
msgid "be verbose; must be placed before a subcommand"
msgstr "chi tiết; phải được đặt trước một lệnh-con"
-msgid "git repack [<options>]"
-msgstr "git repack [<các tùy chọn>]"
+msgid ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
+msgstr ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<tên-pack>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
msgid ""
"Incremental repacks are incompatible with bitmap indexes. Use\n"
@@ -11293,6 +11326,10 @@ msgstr "chuyển --no-reuse-delta cho git-pack-objects"
msgid "pass --no-reuse-object to git-pack-objects"
msgstr "chuyển --no-reuse-object cho git-pack-objects"
+msgid ""
+"specify the name hash version to use for grouping similar objects by path"
+msgstr "phiên bản hàm băm để nhóm đối tượng giống nhau theo đường dẫn"
+
msgid "do not run git-update-server-info"
msgstr "không chạy git-update-server-info"
@@ -11341,9 +11378,6 @@ msgstr "tìm một tiến trình hình học với hệ số <N>"
msgid "write a multi-pack index of the resulting packs"
msgstr "ghi chỉ mục 'multi-pack' của các gói kết quả"
-msgid "pack prefix to store a pack containing pruned objects"
-msgstr "tiền tố của gói để lưu gói gồm những đối tượng đã loại bỏ"
-
msgid "pack prefix to store a pack containing filtered out objects"
msgstr "tiền tố của gói để lưu gói gồm những đối tượng đã lọc bỏ"
@@ -11560,11 +11594,8 @@ msgstr "chỉ được chỉ định một mẫu cùng với tùy chọn -l"
msgid "need some commits to replay"
msgstr "cần các chuyển giao để phát lại"
-msgid "--onto and --advance are incompatible"
-msgstr "--onto và --advance xung khắc"
-
msgid "all positive revisions given must be references"
-msgstr "mọi điểm xét duyệt cộng thêm phải là tên tham chiếu"
+msgstr "mọi lần cải biên cộng thêm phải là tên tham chiếu"
msgid "argument to --advance must be a reference"
msgstr "tham số cho --advance phải là tham chiếu"
@@ -11610,11 +11641,11 @@ msgid ""
"some rev walking options will be overridden as '%s' bit in 'struct rev_info' "
"will be forced"
msgstr ""
-"một số tuỳ chọn duyệt qua điểm xét duyệt sẽ bị bỏ qua do bit '%s' trong "
+"một số tuỳ chọn duyệt qua lần cải biên sẽ bị bỏ qua do bit '%s' trong "
"'struct rev_info' bị ép bật/tắt"
msgid "error preparing revisions"
-msgstr "gặp lỗi khi chuẩn bị các điểm xét duyệt"
+msgstr "gặp lỗi khi chuẩn bị các lần cải biên"
msgid "replaying down to root commit is not supported yet!"
msgstr "chưa hỗ trợ phát lại đến lần chuyển giao gốc!"
@@ -11709,7 +11740,7 @@ msgstr "chỉ ghi lại những đường dẫn thực sự sẽ được thêm
#, c-format
msgid "Failed to resolve '%s' as a valid revision."
-msgstr "Gặp lỗi khi phân giải '%s' thành điểm xét duyệt hợp lệ."
+msgstr "Gặp lỗi khi phân giải '%s' thành lần cải biên hợp lệ."
#, c-format
msgid "Failed to resolve '%s' as a valid tree."
@@ -11742,7 +11773,7 @@ msgstr ""
#, c-format
msgid "Could not reset index file to revision '%s'."
-msgstr "Không thể đặt lại (reset) chỉ mục thành điểm xét duyệt '%s'."
+msgstr "Không thể đặt lại (reset) chỉ mục thành lần cải biên '%s'."
msgid "Could not write new index file."
msgstr "Không thể ghi tập tin chỉ mục mới."
@@ -11784,7 +11815,7 @@ msgid "missing opt-spec before option flags"
msgstr "thiếu opt-spec trước các tuỳ chọn"
msgid "Needed a single revision"
-msgstr "Cần một điểm xét duyệt đơn"
+msgstr "Cần một lần cải biên đơn"
msgid ""
"git rev-parse --parseopt [<options>] -- [<args>...]\n"
@@ -12160,7 +12191,7 @@ msgstr "không có tham chiếu nào như thế %s"
#, c-format
msgid "cannot handle more than %d rev."
msgid_plural "cannot handle more than %d revs."
-msgstr[0] "không thể xử lý nhiều hơn %d điểm xét duyệt."
+msgstr[0] "không thể xử lý nhiều hơn %d lần cải biên."
#, c-format
msgid "'%s' is not a valid ref."
@@ -12438,7 +12469,7 @@ msgstr "'%s' không phải là lần chuyển giao kiểu-stash"
#, c-format
msgid "Too many revisions specified:%s"
-msgstr "Chỉ ra quá nhiều điểm xét duyệt: %s"
+msgstr "Chỉ ra quá nhiều lần cải biên: %s"
msgid "No stash entries found."
msgstr "Không tìm thấy các mục tạm cất (stash) nào."
@@ -12742,7 +12773,7 @@ msgstr ""
"dẫn>]"
msgid "could not fetch a revision for HEAD"
-msgstr "không thể lấy về một điểm xem xét cho HEAD"
+msgstr "không thể lấy về một lần cải biên cho HEAD"
#, c-format
msgid "Synchronizing submodule url for '%s'\n"
@@ -12977,8 +13008,7 @@ msgstr ""
#, c-format
msgid "Unable to find current revision in submodule path '%s'"
-msgstr ""
-"Không tìm thấy điểm xét duyệt hiện hành trong đường dẫn mô-đun-con '%s'"
+msgstr "Không tìm thấy lần cải biên hiện hành trong đường dẫn mô-đun-con '%s'"
#, c-format
msgid "Unable to fetch in submodule path '%s'"
@@ -12986,7 +13016,7 @@ msgstr "Không thể lấy về trong đường dẫn mô-đun-con '%s'"
#, c-format
msgid "Unable to find %s revision in submodule path '%s'"
-msgstr "Không tìm thấy điểm xét duyệt %s trong đường dẫn mô-đun-con '%s'"
+msgstr "Không tìm thấy lần cải biên %s trong đường dẫn mô-đun-con '%s'"
#, c-format
msgid "Failed to recurse into submodule path '%s'"
@@ -13017,8 +13047,7 @@ msgid "use the 'rebase' update strategy"
msgstr "dùng chiến lược hòa trộn 'rebase'"
msgid "create a shallow clone truncated to the specified number of revisions"
-msgstr ""
-"tạo một bản sao nông được cắt ngắn thành số lượng điểm xét duyệt đã cho"
+msgstr "tạo một bản sao nông được cắt ngắn thành số lượng lần cải biên đã cho"
msgid "parallel jobs"
msgstr "công việc đồng thời"
@@ -14190,6 +14219,9 @@ msgstr "Nhập một kho GNU Arch vào một kho Git"
msgid "Create an archive of files from a named tree"
msgstr "Tạo một kho nén các tập tin từ cây làm việc có tên"
+msgid "Download missing objects in a partial clone"
+msgstr "Tải về các đối tượng còn thiếu khi nhân bản một phần"
+
msgid "Use binary search to find the commit that introduced a bug"
msgstr "Tìm kiếm dạng nhị phân để tìm ra lần chuyển giao nào đưa ra lỗi"
@@ -14727,7 +14759,7 @@ msgid "Git Repository Layout"
msgstr "Bố cục kho Git"
msgid "Specifying revisions and ranges for Git"
-msgstr "Chỉ định điểm xét duyệt và vùng cho Git"
+msgstr "Chỉ định lần cải biên và vùng cho Git"
msgid "Mounting one repository inside another"
msgstr "Gắn một kho chứa vào trong một cái khác"
@@ -15482,7 +15514,7 @@ msgstr "tham chiếu '%s' không chỉ đến một blob nào cả"
#, c-format
msgid "unable to resolve config blob '%s'"
-msgstr "không thể phân giải điểm xét duyệt '%s'"
+msgstr "không thể phân giải lần cải biên '%s'"
msgid "unable to parse command-line config"
msgstr "không thể đọc cấu hình dòng lệnh"
@@ -16106,6 +16138,12 @@ msgstr "tham số cho %s không hợp lệ"
msgid "invalid regex given to -I: '%s'"
msgstr "đưa cho -I biểu thức chính quy không hợp lệ: '%s'"
+msgid "-G requires a non-empty argument"
+msgstr "-G cần một tham số"
+
+msgid "-S requires a non-empty argument"
+msgstr "-S cần một tham số"
+
#, c-format
msgid "failed to parse --submodule option parameter: '%s'"
msgstr "gặp lỗi khi đọc đối số tùy chọn --submodule: '%s'"
@@ -18275,6 +18313,10 @@ msgid "unable to write file %s"
msgstr "không thể ghi tập tin %s"
#, c-format
+msgid "unable to write repeatedly vanishing file %s"
+msgstr "không thể ghi vào tập tin biến mất liên tục %s"
+
+#, c-format
msgid "unable to set permission to '%s'"
msgstr "không thể đặt quyền thành '%s'"
@@ -18735,7 +18777,7 @@ msgstr "checksum không hợp lệ"
#, c-format
msgid "invalid rev-index position at %<PRIu64>: %<PRIu32> != %<PRIu32>"
-msgstr "vị trí mục xét duyệt không hợp lệ %<PRIu64>: %<PRIu32> != %<PRIu32>"
+msgstr "vị trí mục cải biên không hợp lệ %<PRIu64>: %<PRIu32> != %<PRIu32>"
msgid "multi-pack-index reverse-index chunk is the wrong size"
msgstr "chunk chỉ mục ngược của chỉ mục đa gói có kích thước sai"
@@ -18833,6 +18875,52 @@ msgstr "không hiểu tùy chọn '%c'"
msgid "unknown non-ascii option in string: `%s'"
msgstr "không hiểu tùy chọn non-ascii trong chuỗi: '%s'"
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the long form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[=<%s>]"
+msgstr "[=<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the short form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid "[<%s>]"
+msgstr "[<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string stands for a
+#. value given to a command line option, and "<>" is there
+#. as a convention to signal that it is a placeholder
+#. (i.e. the user should substitute it with the real value).
+#. If your language uses a different convention, you can
+#. change "<%s>" part to match yours, e.g. it might use
+#. "|%s|" instead, or if the alphabet is different enough it
+#. may use "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#, c-format
+msgid " <%s>"
+msgstr " <%s>"
+
msgid "..."
msgstr "..."
@@ -18920,6 +19008,21 @@ msgid "failed to parse %s"
msgstr "gặp lỗi khi đọc cú pháp %s"
#, c-format
+msgid "failed to walk children of tree %s: not found"
+msgstr "gặp lỗi khi duyệt nhánh của cây %s: không tìm thấy"
+
+#, c-format
+msgid "failed to find object %s"
+msgstr "gặp lỗi khi tìm đối tượng '%s'."
+
+#, c-format
+msgid "failed to find tag %s"
+msgstr "gặp lỗi khi tìm thẻ %s"
+
+msgid "failed to setup revision walk"
+msgstr "gặp lỗi khi thiết lập duyệt lần cải biên"
+
+#, c-format
msgid "Could not make %s writable by group"
msgstr "Không thể làm %s được ghi bởi nhóm"
@@ -19063,6 +19166,22 @@ msgstr "tên máy chủ promisor không thể bắt đầu bằng '/': %s"
msgid "could not fetch %s from promisor remote"
msgstr "không thể tải %s từ máy chủ promisor"
+#, c-format
+msgid "known remote named '%s' but with url '%s' instead of '%s'"
+msgstr "có máy chủ '%s' nhưng với url '%s' thay vì '%s'"
+
+#, c-format
+msgid "unknown '%s' value for '%s' config option"
+msgstr "không hiểu giá trị '%s' cho cho cấu hình '%s'"
+
+#, c-format
+msgid "unknown element '%s' from remote info"
+msgstr "không hiểu phần '%s' từ thông tin máy chủ"
+
+#, c-format
+msgid "accepted promisor remote '%s' not found"
+msgstr "không tìm thấy accepted promisor remote '%s'"
+
msgid "object-info: expected flush after arguments"
msgstr "object-info: cần đẩy dữ liệu lên đĩa sau các tham số"
@@ -19750,7 +19869,7 @@ msgid "refusing to update ref with bad name '%s'"
msgstr "từ chối cập nhật tham chiếu với tên sai '%s'"
msgid "refusing to force and skip creation of reflog"
-msgstr "từ chối bỏ qua việc tạo log tham chiếu"
+msgstr "từ chối bỏ qua việc tạo reflog"
#, c-format
msgid "update_ref failed for ref '%s': %s"
@@ -19884,6 +20003,14 @@ msgid "invalid refspec '%s'"
msgstr "refspec không hợp lệ '%s'"
#, c-format
+msgid "pattern '%s' has no '*'"
+msgstr "giá trị '%s' không có '*'"
+
+#, c-format
+msgid "replacement '%s' has no '*'"
+msgstr "thay thế '%s' không có '*'"
+
+#, c-format
msgid "invalid quoting in push-option value: '%s'"
msgstr "sai trích dẫn trong giá trị push-option :'%s'"
@@ -20003,6 +20130,27 @@ msgid "remote-curl: unknown command '%s' from git"
msgstr "remote-curl: không hiểu lệnh '%s' từ git"
#, c-format
+msgid ""
+"reading remote from \"%s/%s\", which is nominated for removal.\n"
+"\n"
+"If you still use the \"remotes/\" directory it is recommended to\n"
+"migrate to config-based remotes:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"If you cannot, please let us know why you still need to use it by\n"
+"sending an e-mail to <git@vger.kernel.org>."
+msgstr ""
+"đang đọc máy chủ từ \"%s/%s\", đã được đề cử để loại bỏ.\n"
+"Nếu bạn vẫn còn sử dụng thư mục \"remotes\", chúng tôi khuyên bạn\n"
+"hãy chuyển đổi sang sử dụng máy chủ qua tập tin cấu hình:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"Nếu bạn không thể đổi, hãy cho chúng tôi biết tại sao bạn cần nó\n"
+"bằng cách gửi e-mail đến <git@vger.kernel.org>."
+
+#, c-format
msgid "config remote shorthand cannot begin with '/': %s"
msgstr "cấu hình viết tắt máy chủ không thể bắt đầu bằng '/': %s"
@@ -20037,14 +20185,6 @@ msgid "%s tracks both %s and %s"
msgstr "%s theo dõi cả %s và %s"
#, c-format
-msgid "key '%s' of pattern had no '*'"
-msgstr "khóa '%s' của mẫu k có '*'"
-
-#, c-format
-msgid "value '%s' of pattern has no '*'"
-msgstr "giá trị '%s' của mẫu k có '*'"
-
-#, c-format
msgid "src refspec %s does not match any"
msgstr "refspec (đặc tả tham chiếu) nguồn %s không khớp bất kỳ cái gì"
@@ -20317,7 +20457,7 @@ msgid "update the index with reused conflict resolution if possible"
msgstr "cập nhật chỉ mục với phân giải xung đột dùng lại nếu được"
msgid "could not determine HEAD revision"
-msgstr "không thể dò tìm điểm xét duyệt HEAD"
+msgstr "không thể dò tìm lần cải biên HEAD"
#, c-format
msgid "failed to find tree of %s"
@@ -21456,10 +21596,10 @@ msgid ""
"Use '--' to separate paths from revisions, like this:\n"
"'git <command> [<revision>...] -- [<file>...]'"
msgstr ""
-"tham số chưa rõ ràng '%s': chưa biết điểm xét duyệt hay đường dẫn không "
-"trong cây làm việc.\n"
-"Dùng '--' để ngăn cách các đường dẫn khỏi điểm xét duyệt, như thế này:\n"
-"'git <lệnh> [<điểm xét duyệt>...] -- [<tập tin>...]'"
+"tham số chưa rõ ràng '%s': chưa biết lần cải biên hay đường dẫn không trong "
+"cây làm việc.\n"
+"Dùng '--' để ngăn cách các đường dẫn khỏi lần cải biên, như thế này:\n"
+"'git <lệnh> [<lần cải biên>...] -- [<tập tin>...]'"
#, c-format
msgid "option '%s' must come before non-option arguments"
@@ -21471,9 +21611,9 @@ msgid ""
"Use '--' to separate paths from revisions, like this:\n"
"'git <command> [<revision>...] -- [<file>...]'"
msgstr ""
-"tham số chưa rõ ràng '%s': cả điểm xem xét và tên tập tin.\n"
-"Dùng '--' để ngăn cách các đường dẫn khỏi điểm xem xét, như thế này:\n"
-"'git <lệnh> [<điểm xem xét>...] -- [<tập tin>...]'"
+"tham số chưa rõ ràng '%s': cả lần cải biên và tên tập tin.\n"
+"Dùng '--' để ngăn cách các đường dẫn khỏi lần cải biên, như thế này:\n"
+"'git <lệnh> [<lần cải biên>...] -- [<tập tin>...]'"
msgid "unable to set up work tree using invalid config"
msgstr "không thể thiết lập thư mục làm việc với cấu hình không hợp lệ"
@@ -21935,6 +22075,27 @@ msgstr "dọn cây nhớ tạm trước mỗi chu kỳ"
msgid "number of entries in the cache tree to invalidate (default 0)"
msgstr "số mục cần huỷ trong câu nhớ tạm (mặc định 0)"
+msgid "test-tool path-walk <options> -- <revision-options>"
+msgstr "test-tool path-walk <tuỳ chọn> -- <tuỳ chọn cải biên>"
+
+msgid "toggle inclusion of blob objects"
+msgstr "bao gồm các đối tượng blob"
+
+msgid "toggle inclusion of commit objects"
+msgstr "bao gồm các đối tượng chuyển giao"
+
+msgid "toggle inclusion of tag objects"
+msgstr "bao gồm các đối tượng thẻ"
+
+msgid "toggle inclusion of tree objects"
+msgstr "bao gồm các đối tượng cây"
+
+msgid "toggle pruning of uninteresting paths"
+msgstr "lược bỏ những đường dẫn ít ý nghĩa"
+
+msgid "read a pattern list over stdin"
+msgstr "đọc các mẫu từ stdin"
+
#, c-format
msgid "commit %s is not marked reachable"
msgstr "lần chuyển giao %s chưa được đánh dấu là tiếp cận được"
@@ -22579,6 +22740,10 @@ msgstr "lỗi: "
msgid "warning: "
msgstr "cảnh báo: "
+#, c-format
+msgid "uname() failed with error '%s' (%d)\n"
+msgstr "uname() gặp lỗi '%s' (%d)\n"
+
msgid "Fetching objects"
msgstr "Đang lấy về các đối tượng"
@@ -23536,3 +23701,21 @@ msgstr "Bỏ qua %s với hậu tố sao lưu '%s'.\n"
#, perl-format
msgid "Do you really want to send %s? [y|N]: "
msgstr "Bạn có thực sự muốn gửi %s? [y|N](có/KHÔNG): "
+
+#, c-format
+#~ msgid "Could not find remote branch %s to clone."
+#~ msgstr "Không tìm thấy nhánh máy chủ %s để nhân bản (clone)."
+
+#, c-format
+#~ msgid "merging cannot continue; got unclean result of %d"
+#~ msgstr "không thể tiếp tục hoà trộn; kết quả không hoàn toàn %d"
+
+#~ msgid "git repack [<options>]"
+#~ msgstr "git repack [<các tùy chọn>]"
+
+#~ msgid "--onto and --advance are incompatible"
+#~ msgstr "--onto và --advance xung khắc"
+
+#, c-format
+#~ msgid "key '%s' of pattern had no '*'"
+#~ msgstr "khóa '%s' của mẫu k có '*'"
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 12a0fb510b..5cde4011e7 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -93,6 +93,7 @@
# pack index | 包索引
# packfile | 包文件
# parent | 父提交
+# partial clone | 部分克隆
# patch | 补丁
# pathspec | 路径规格
# pattern | 模式
@@ -154,8 +155,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Git\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2025-01-02 20:43+0800\n"
-"PO-Revision-Date: 2025-01-05 19:01+0800\n"
+"POT-Creation-Date: 2025-03-09 20:34+0800\n"
+"PO-Revision-Date: 2025-03-12 14:47+0800\n"
"Last-Translator: Teng Long <dyroneteng@gmail.com>\n"
"Language-Team: GitHub <https://github.com/dyrone/git/>\n"
"Language: zh_CN\n"
@@ -2962,6 +2963,22 @@ msgstr "git archive:协议错误"
msgid "git archive: expected a flush"
msgstr "git archive:应有一个 flush 包"
+#: builtin/backfill.c
+msgid "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+msgstr "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+
+#: builtin/backfill.c
+msgid "problem loading sparse-checkout"
+msgstr "加载稀疏检出时出现问题"
+
+#: builtin/backfill.c
+msgid "Minimum number of objects to request at a time"
+msgstr "单次请求的最少对象数量"
+
+#: builtin/backfill.c
+msgid "Restrict the missing objects to the current sparse-checkout"
+msgstr "将缺少的对象限制在当前的稀疏检出中"
+
#: builtin/bisect.c
msgid ""
"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>] [--no-"
@@ -3381,7 +3398,7 @@ msgstr "显示作者的邮箱而不是名字(默认:关闭)"
msgid "ignore whitespace differences"
msgstr "忽略空白差异"
-#: builtin/blame.c builtin/log.c
+#: builtin/blame.c builtin/clone.c builtin/log.c
msgid "rev"
msgstr "版本"
@@ -3803,8 +3820,8 @@ msgstr "HEAD 没有位于 /refs/heads 之下!"
#: builtin/branch.c
msgid ""
-"branch with --recurse-submodules can only be used if submodule."
-"propagateBranches is enabled"
+"branch with --recurse-submodules can only be used if "
+"submodule.propagateBranches is enabled"
msgstr ""
"带有 --recurse-submodules 的分支只能在 submodule.propagateBranches 启用时使用"
@@ -3892,11 +3909,6 @@ msgid "git version:\n"
msgstr "git 版本:\n"
#: builtin/bugreport.c
-#, c-format
-msgid "uname() failed with error '%s' (%d)\n"
-msgstr "uname() 失败,错误为 '%s'(%d)\n"
-
-#: builtin/bugreport.c
msgid "compiler info: "
msgstr "编译器信息:"
@@ -5134,8 +5146,112 @@ msgid "clean.requireForce is true and -f not given: refusing to clean"
msgstr "clean.requireForce 设置为 true 且未提供 -f 选项:拒绝执行清理动作"
#: builtin/clone.c
-msgid "git clone [<options>] [--] <repo> [<dir>]"
-msgstr "git clone [<选项>] [--] <仓库> [<路径>]"
+#, c-format
+msgid "info: Could not add alternate for '%s': %s\n"
+msgstr "info: 不能为 '%s' 添加一个备用:%s\n"
+
+#: builtin/clone.c builtin/diff.c builtin/rm.c grep.c setup.c
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "无法对 '%s' 调用 stat"
+
+#: builtin/clone.c
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s 存在且不是一个目录"
+
+#: builtin/clone.c
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "'%s' 为符号链接,拒绝用 --local 克隆"
+
+#: builtin/clone.c
+#, c-format
+msgid "failed to start iterator over '%s'"
+msgstr "无法在 '%s' 上启动迭代器"
+
+#: builtin/clone.c
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr "符号链接 '%s' 存在,拒绝用 --local 克隆"
+
+#: builtin/clone.c compat/precompose_utf8.c
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "无法删除 '%s'"
+
+#: builtin/clone.c
+#, c-format
+msgid "hardlink cannot be checked at '%s'"
+msgstr "无法检查 '%s' 处的硬链接"
+
+#: builtin/clone.c
+#, c-format
+msgid "hardlink different from source at '%s'"
+msgstr "硬链接与 '%s' 处的来源不同"
+
+#: builtin/clone.c
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "无法创建链接 '%s'"
+
+#: builtin/clone.c
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "无法拷贝文件至 '%s'"
+
+#: builtin/clone.c refs/files-backend.c
+#, c-format
+msgid "failed to iterate over '%s'"
+msgstr "无法在 '%s' 上迭代"
+
+#: builtin/clone.c
+#, c-format
+msgid "done.\n"
+msgstr "完成。\n"
+
+#: builtin/clone.c
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry with 'git restore --source=HEAD :/'\n"
+msgstr ""
+"克隆成功,但是检出失败。\n"
+"您可以通过 'git status' 检查哪些已被检出,然后使用命令\n"
+"'git restore --source=HEAD :/' 重试\n"
+
+#: builtin/clone.c fetch-pack.c
+msgid "remote did not send all necessary objects"
+msgstr "远程没有发送所有必需的对象"
+
+#: builtin/clone.c
+#, c-format
+msgid "unable to update %s"
+msgstr "不能更新 %s"
+
+#: builtin/clone.c
+msgid "failed to initialize sparse-checkout"
+msgstr "无法初始化稀疏检出"
+
+#: builtin/clone.c
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
+msgstr "远程 HEAD 指向一个不存在的引用,无法检出"
+
+#: builtin/clone.c
+msgid "unable to checkout working tree"
+msgstr "不能检出工作区"
+
+#: builtin/clone.c
+msgid "unable to write parameters to config file"
+msgstr "无法将参数写入配置文件"
+
+#: builtin/clone.c
+msgid "cannot repack to clean up"
+msgstr "无法执行 repack 来清理"
+
+#: builtin/clone.c
+msgid "cannot unlink temporary alternates file"
+msgstr "无法删除临时的备用文件"
#: builtin/clone.c
msgid "don't clone shallow repository"
@@ -5208,6 +5324,10 @@ msgid "checkout <branch> instead of the remote's HEAD"
msgstr "检出 <分支> 而不是远程 HEAD"
#: builtin/clone.c
+msgid "clone single revision <rev> and check out"
+msgstr "克隆单个版本 <版本> 并检出"
+
+#: builtin/clone.c
msgid "path to git-upload-pack on the remote"
msgstr "远程 git-upload-pack 路径"
@@ -5236,8 +5356,8 @@ msgid "clone only one branch, HEAD or --branch"
msgstr "只克隆一个分支、HEAD 或 --branch"
#: builtin/clone.c
-msgid "don't clone any tags, and make later fetches not to follow them"
-msgstr "不要克隆任何标签,并且后续获取操作也不下载它们"
+msgid "clone tags, and make later fetches not to follow them"
+msgstr "克隆标签,并在后续获取时不跟随它们"
#: builtin/clone.c
msgid "any cloned submodules will be shallow"
@@ -5294,117 +5414,8 @@ msgid "a URI for downloading bundles before fetching from origin remote"
msgstr "用于在从 origin 远程获取之前下载归档包的 URI"
#: builtin/clone.c
-#, c-format
-msgid "info: Could not add alternate for '%s': %s\n"
-msgstr "info: 不能为 '%s' 添加一个备用:%s\n"
-
-#: builtin/clone.c builtin/diff.c builtin/rm.c grep.c setup.c
-#, c-format
-msgid "failed to stat '%s'"
-msgstr "无法对 '%s' 调用 stat"
-
-#: builtin/clone.c
-#, c-format
-msgid "%s exists and is not a directory"
-msgstr "%s 存在且不是一个目录"
-
-#: builtin/clone.c
-#, c-format
-msgid "'%s' is a symlink, refusing to clone with --local"
-msgstr "'%s' 为符号链接,拒绝用 --local 克隆"
-
-#: builtin/clone.c
-#, c-format
-msgid "failed to start iterator over '%s'"
-msgstr "无法在 '%s' 上启动迭代器"
-
-#: builtin/clone.c
-#, c-format
-msgid "symlink '%s' exists, refusing to clone with --local"
-msgstr "符号链接 '%s' 存在,拒绝用 --local 克隆"
-
-#: builtin/clone.c compat/precompose_utf8.c
-#, c-format
-msgid "failed to unlink '%s'"
-msgstr "无法删除 '%s'"
-
-#: builtin/clone.c
-#, c-format
-msgid "hardlink cannot be checked at '%s'"
-msgstr "无法检查 '%s' 处的硬链接"
-
-#: builtin/clone.c
-#, c-format
-msgid "hardlink different from source at '%s'"
-msgstr "硬链接与 '%s' 处的源不同"
-
-#: builtin/clone.c
-#, c-format
-msgid "failed to create link '%s'"
-msgstr "无法创建链接 '%s'"
-
-#: builtin/clone.c
-#, c-format
-msgid "failed to copy file to '%s'"
-msgstr "无法拷贝文件至 '%s'"
-
-#: builtin/clone.c refs/files-backend.c
-#, c-format
-msgid "failed to iterate over '%s'"
-msgstr "无法在 '%s' 上迭代"
-
-#: builtin/clone.c
-#, c-format
-msgid "done.\n"
-msgstr "完成。\n"
-
-#: builtin/clone.c
-msgid ""
-"Clone succeeded, but checkout failed.\n"
-"You can inspect what was checked out with 'git status'\n"
-"and retry with 'git restore --source=HEAD :/'\n"
-msgstr ""
-"克隆成功,但是检出失败。\n"
-"您可以通过 'git status' 检查哪些已被检出,然后使用命令\n"
-"'git restore --source=HEAD :/' 重试\n"
-
-#: builtin/clone.c
-#, c-format
-msgid "Could not find remote branch %s to clone."
-msgstr "不能发现要克隆的远程分支 %s。"
-
-#: builtin/clone.c fetch-pack.c
-msgid "remote did not send all necessary objects"
-msgstr "远程没有发送所有必需的对象"
-
-#: builtin/clone.c
-#, c-format
-msgid "unable to update %s"
-msgstr "不能更新 %s"
-
-#: builtin/clone.c
-msgid "failed to initialize sparse-checkout"
-msgstr "无法初始化稀疏检出"
-
-#: builtin/clone.c
-msgid "remote HEAD refers to nonexistent ref, unable to checkout"
-msgstr "远程 HEAD 指向一个不存在的引用,无法检出"
-
-#: builtin/clone.c
-msgid "unable to checkout working tree"
-msgstr "不能检出工作区"
-
-#: builtin/clone.c
-msgid "unable to write parameters to config file"
-msgstr "无法将参数写入配置文件"
-
-#: builtin/clone.c
-msgid "cannot repack to clean up"
-msgstr "无法执行 repack 来清理"
-
-#: builtin/clone.c
-msgid "cannot unlink temporary alternates file"
-msgstr "无法删除临时的 alternates 文件"
+msgid "git clone [<options>] [--] <repo> [<dir>]"
+msgstr "git clone [<选项>] [--] <仓库> [<目录>]"
#: builtin/clone.c
msgid "Too many arguments."
@@ -5531,6 +5542,11 @@ msgid "Remote branch %s not found in upstream %s"
msgstr "远程分支 %s 在上游 %s 未发现"
#: builtin/clone.c
+#, c-format
+msgid "Remote revision %s not found in upstream %s"
+msgstr "在上游 %2$s 未发现远程版本 %1$s"
+
+#: builtin/clone.c
msgid "You appear to have cloned an empty repository."
msgstr "您似乎克隆了一个空仓库。"
@@ -5593,7 +5609,8 @@ msgstr ""
"[no-]progress]\n"
" <切分选项>"
-#: builtin/commit-graph.c builtin/fetch.c builtin/log.c builtin/repack.c
+#: builtin/commit-graph.c builtin/fetch.c builtin/gc.c builtin/log.c
+#: builtin/repack.c
msgid "dir"
msgstr "目录"
@@ -5751,7 +5768,7 @@ msgstr "git commit-tree:无法读取"
#: builtin/commit.c
msgid ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
"reword):]<commit>]\n"
" [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
@@ -5761,7 +5778,7 @@ msgid ""
" [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
" [--] [<pathspec>...]"
msgstr ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<模式>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<模式>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <提交> | --fixup [(amend|"
"reword):]<提交>]\n"
" [-F <文件> | -m <消息>] [--reset-author] [--allow-empty]\n"
@@ -7473,14 +7490,13 @@ msgid ""
"Run 'git remote set-head %s %s' to follow the change, or set\n"
"'remote.%s.followRemoteHEAD' configuration option to a different value\n"
"if you do not want to see this message. Specifically running\n"
-"'git config set remote.%s.followRemoteHEAD %s' will disable the warning\n"
-"until the remote changes HEAD to something else."
+"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n"
+"will disable the warning until the remote changes HEAD to something else."
msgstr ""
-"运行 'git remote set-head %s %s' 以跟随更改,或者\n"
-"如果您不想看到此消息,则将'remote.%s.followRemoteHEAD' 配置选项设置为不同的"
-"值。\n"
-"特别地,运行 'git config set remote.%s.followRemoteHEAD %s' 将禁用警告,直到"
-"远程将 HEAD 更改为其他内容。\""
+"运行 'git remote set-head %s %s' 同步变更,或通过配置选项\n"
+"'remote.%s.followRemoteHEAD' 设置为其他值以屏蔽此提示。具体可通过\n"
+"运行命令 'git config remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n"
+"以禁用该警告,直到远程将 HEAD 更改为其他内容。"
#: builtin/fetch.c
msgid "multiple branches detected, incompatible with --set-upstream"
@@ -7721,8 +7737,8 @@ msgstr "协议不支持 --negotiate-only,退出"
#: builtin/fetch.c
msgid ""
-"--filter can only be used with the remote configured in extensions."
-"partialclone"
+"--filter can only be used with the remote configured in "
+"extensions.partialclone"
msgstr "只可以将 --filter 用于在 extensions.partialclone 中配置的远程仓库"
#: builtin/fetch.c
@@ -8328,6 +8344,10 @@ msgstr "强制执行 gc 即使另外一个 gc 正在执行"
msgid "repack all other packs except the largest pack"
msgstr "除了最大的包之外,对所有其它包文件重新打包"
+#: builtin/gc.c builtin/repack.c
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr "用于存储修剪对象的包前缀"
+
#: builtin/gc.c
#, c-format
msgid "failed to parse gc.logExpiry value %s"
@@ -9327,6 +9347,11 @@ msgstr "无法完成 pack-objects 来重新打包本地链接"
msgid "Cannot come back to cwd"
msgstr "无法返回当前工作目录"
+#: builtin/index-pack.c builtin/unpack-objects.c
+#, c-format
+msgid "bad --pack_header: %s"
+msgstr "错误的 --pack_header:%s"
+
#: builtin/index-pack.c
#, c-format
msgid "bad %s"
@@ -10432,11 +10457,6 @@ msgstr "未知的策略选项:-X%s"
msgid "malformed input line: '%s'."
msgstr "格式错误的输入行:'%s'。"
-#: builtin/merge-tree.c
-#, c-format
-msgid "merging cannot continue; got unclean result of %d"
-msgstr "合并无法继续;得到不干净的结果 %d"
-
#: builtin/merge.c
msgid "git merge [<options>] [<commit>...]"
msgstr "git merge [<选项>] [<提交>...]"
@@ -11454,6 +11474,15 @@ msgstr "git pack-objects [<选项>] <前缀名称> [< <引用列表> | < <对象
#: builtin/pack-objects.c
#, c-format
+msgid "invalid --name-hash-version option: %d"
+msgstr "无效的 --name-hash-version 选项:%d"
+
+#: builtin/pack-objects.c
+msgid "currently, --write-bitmap-index requires --name-hash-version=1"
+msgstr "当前,--write-bitmap-index 要求 --name-hash-version=1"
+
+#: builtin/pack-objects.c
+#, c-format
msgid ""
"write_reuse_object: could not locate %s, expected at offset %<PRIuMAX> in "
"pack %s"
@@ -11857,7 +11886,11 @@ msgstr "协议"
#: builtin/pack-objects.c
msgid "exclude any configured uploadpack.blobpackfileuri with this protocol"
-msgstr "使用此协议排除任何已配置的 uploadpack.blobpackfileuri"
+msgstr "排除掉采用该协议的 uploadpack.blobpackfileuri 配置项"
+
+#: builtin/pack-objects.c
+msgid "use the specified name-hash function to group similar objects"
+msgstr "使用指定的名称哈希函数对相似的对象进行分组"
#: builtin/pack-objects.c
#, c-format
@@ -12284,8 +12317,8 @@ msgid ""
"upstream, see 'push.autoSetupRemote' in 'git help config'.\n"
msgstr ""
"\n"
-"为了让没有追踪上游的分支自动配置,参见 'git help config' 中的 push."
-"autoSetupRemote。\n"
+"为了让没有追踪上游的分支自动配置,参见 'git help config' 中的 "
+"push.autoSetupRemote。\n"
#: builtin/push.c
#, c-format
@@ -13294,8 +13327,8 @@ msgid "invalid ref format: %s"
msgstr "无效的引用格式:%s"
#: builtin/refs.c
-msgid "git refs migrate --ref-format=<format> [--dry-run]"
-msgstr "git refs migrate --ref-format=<格式> [--dry-run]"
+msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
+msgstr "git refs migrate --ref-format=<格式> [--no-reflog] [--dry-run]"
#: builtin/refs.c
msgid "git refs verify [--strict] [--verbose]"
@@ -13310,6 +13343,10 @@ msgid "perform a non-destructive dry-run"
msgstr "进行非破坏性的试运行(dry-run)"
#: builtin/refs.c
+msgid "drop reflogs entirely during the migration"
+msgstr "在迁移期间丢弃引用日志"
+
+#: builtin/refs.c
msgid "missing --ref-format=<format>"
msgstr "缺少 --ref-format=<格式>"
@@ -13881,8 +13918,14 @@ msgid "be verbose; must be placed before a subcommand"
msgstr "冗长输出;必须置于子命令之前"
#: builtin/repack.c
-msgid "git repack [<options>]"
-msgstr "git repack [<选项>]"
+msgid ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
+msgstr ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<包名>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
#: builtin/repack.c
msgid ""
@@ -13975,6 +14018,11 @@ msgid "pass --no-reuse-object to git-pack-objects"
msgstr "向 git-pack-objects 传递参数 --no-reuse-object"
#: builtin/repack.c
+msgid ""
+"specify the name hash version to use for grouping similar objects by path"
+msgstr "指定要使用的名称哈希算法,以实现按照路径对相似对象分组"
+
+#: builtin/repack.c
msgid "do not run git-update-server-info"
msgstr "不运行 git-update-server-info"
@@ -14039,10 +14087,6 @@ msgid "write a multi-pack index of the resulting packs"
msgstr "写入结果包的多包索引"
#: builtin/repack.c
-msgid "pack prefix to store a pack containing pruned objects"
-msgstr "储存被清除的对象的包的前缀"
-
-#: builtin/repack.c
msgid "pack prefix to store a pack containing filtered out objects"
msgstr "储存被过滤的对象的包的前缀"
@@ -14315,10 +14359,6 @@ 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 "提供的所有正向版本必须为引用"
@@ -17546,6 +17586,10 @@ msgid "Create an archive of files from a named tree"
msgstr "基于一个指定的树创建文件存档"
#: command-list.h
+msgid "Download missing objects in a partial clone"
+msgstr "下载部分克隆中缺失的对象"
+
+#: command-list.h
msgid "Use binary search to find the commit that introduced a bug"
msgstr "通过二分查找定位引入 bug 的提交"
@@ -18927,8 +18971,8 @@ msgid ""
"remote URLs cannot be configured in file directly or indirectly included by "
"includeIf.hasconfig:remote.*.url"
msgstr ""
-"远程 URL 不能在文件中配置,不管直接地还是通过 includeIf.hasconfig:remote.*."
-"url 间接地包含。"
+"远程 URL 不能在文件中配置,不管直接地还是通过 "
+"includeIf.hasconfig:remote.*.url 间接地包含。"
#: config.c
#, c-format
@@ -19926,6 +19970,14 @@ msgid "invalid regex given to -I: '%s'"
msgstr "选项 -I 的正则表达式无效:'%s'"
#: diff.c
+msgid "-G requires a non-empty argument"
+msgstr "-G 需要一个非空参数"
+
+#: diff.c
+msgid "-S requires a non-empty argument"
+msgstr "-S 需要一个非空参数"
+
+#: diff.c
#, c-format
msgid "failed to parse --submodule option parameter: '%s'"
msgstr "无法解析 --submodule 选项的参数:'%s'"
@@ -22430,7 +22482,7 @@ msgstr "无法读取替代文件"
#: object-file.c
msgid "unable to move new alternates file into place"
-msgstr "无法将新的替代文件移动到位"
+msgstr "无法将新的备用文件移动到位"
#: object-file.c
#, c-format
@@ -22553,6 +22605,11 @@ msgstr "无法写文件 %s"
#: object-file.c
#, c-format
+msgid "unable to write repeatedly vanishing file %s"
+msgstr "无法写入反复消失的文件 %s"
+
+#: object-file.c
+#, c-format
msgid "unable to set permission to '%s'"
msgstr "无法为 '%s' 设置权限"
@@ -23233,6 +23290,55 @@ msgstr "未知开关 `%c'"
msgid "unknown non-ascii option in string: `%s'"
msgstr "字符串中未知的非 ascii 字符选项:`%s'"
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the long form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#: parse-options.c
+#, c-format
+msgid "[=<%s>]"
+msgstr "[=<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the short form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#: parse-options.c
+#, c-format
+msgid "[<%s>]"
+msgstr "[<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string stands for a
+#. value given to a command line option, and "<>" is there
+#. as a convention to signal that it is a placeholder
+#. (i.e. the user should substitute it with the real value).
+#. If your language uses a different convention, you can
+#. change "<%s>" part to match yours, e.g. it might use
+#. "|%s|" instead, or if the alphabet is different enough it
+#. may use "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#: parse-options.c
+#, c-format
+msgid " <%s>"
+msgstr " <%s>"
+
#: parse-options.c
msgid "..."
msgstr "..."
@@ -23336,6 +23442,25 @@ msgstr "对于 '%2$s' 的错误的布尔环境取值 '%1$s'"
msgid "failed to parse %s"
msgstr "无法解析 %s"
+#: path-walk.c
+#, c-format
+msgid "failed to walk children of tree %s: not found"
+msgstr "无法遍历树 %s 的子节点:未找到"
+
+#: path-walk.c
+#, c-format
+msgid "failed to find object %s"
+msgstr "无法找到对象 %s"
+
+#: path-walk.c
+#, c-format
+msgid "failed to find tag %s"
+msgstr "无法找到标签 %s"
+
+#: path-walk.c
+msgid "failed to setup revision walk"
+msgstr "无法设置版本遍历"
+
#: path.c
#, c-format
msgid "Could not make %s writable by group"
@@ -23517,6 +23642,26 @@ msgstr "promisor 远程名称不能以 '/' 开始:%s"
msgid "could not fetch %s from promisor remote"
msgstr "无法从承诺者远程获取 %s"
+#: promisor-remote.c
+#, c-format
+msgid "known remote named '%s' but with url '%s' instead of '%s'"
+msgstr "已知远程名称为 '%s',但 url 为 '%s' 而不是 '%s'"
+
+#: promisor-remote.c
+#, c-format
+msgid "unknown '%s' value for '%s' config option"
+msgstr "配置项 '%2$s' 为未知的取值 '%1$s'"
+
+#: promisor-remote.c
+#, c-format
+msgid "unknown element '%s' from remote info"
+msgstr "远程信息中的未知元素 '%s'"
+
+#: promisor-remote.c
+#, c-format
+msgid "accepted promisor remote '%s' not found"
+msgstr "未找到已接受的承诺者远程 '%s'"
+
#: protocol-caps.c
msgid "object-info: expected flush after arguments"
msgstr "object-info:在参数之后应有一个 flush"
@@ -24484,6 +24629,16 @@ msgstr "引用名 %s 是一个符号引用,不支持复制"
msgid "invalid refspec '%s'"
msgstr "无效的引用规格:'%s'"
+#: refspec.c
+#, c-format
+msgid "pattern '%s' has no '*'"
+msgstr "模式 '%s' 没有 '*'"
+
+#: refspec.c
+#, c-format
+msgid "replacement '%s' has no '*'"
+msgstr "替换 '%s' 没有 '*'"
+
#: remote-curl.c
#, c-format
msgid "invalid quoting in push-option value: '%s'"
@@ -24637,6 +24792,28 @@ msgstr "remote-curl:未知的来自 git 的命令 '%s'"
#: remote.c
#, c-format
+msgid ""
+"reading remote from \"%s/%s\", which is nominated for removal.\n"
+"\n"
+"If you still use the \"remotes/\" directory it is recommended to\n"
+"migrate to config-based remotes:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"If you cannot, please let us know why you still need to use it by\n"
+"sending an e-mail to <git@vger.kernel.org>."
+msgstr ""
+"正在从 \"%s/%s\" 读取远程内容,该内容被提名删除。\n"
+"\n"
+"如果你仍然在使用 \"remotes/\" 目录,建议迁移为基于配置远程的方式:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"如果你无法迁移,请发送电子邮件至 <git@vger.kernel.org> 告知我们你\n"
+"仍然需要使用它的原因。"
+
+#: remote.c
+#, c-format
msgid "config remote shorthand cannot begin with '/': %s"
msgstr "配置的远程短名称不能以 '/' 开始:%s"
@@ -24680,16 +24857,6 @@ msgstr "%s 同时跟踪 %s 和 %s"
#: remote.c
#, c-format
-msgid "key '%s' of pattern had no '*'"
-msgstr "模式的键 '%s' 没有 '*'"
-
-#: remote.c
-#, c-format
-msgid "value '%s' of pattern has no '*'"
-msgstr "模式的值 '%s' 没有 '*'"
-
-#: remote.c
-#, c-format
msgid "src refspec %s does not match any"
msgstr "源引用规格 %s 没有匹配"
@@ -26956,6 +27123,34 @@ msgstr "在每次迭代前清除缓存树"
msgid "number of entries in the cache tree to invalidate (default 0)"
msgstr "缓存树中无效化的条目数量(默认 0)"
+#: t/helper/test-path-walk.c
+msgid "test-tool path-walk <options> -- <revision-options>"
+msgstr "test-tool path-walk <选项> -- <版本选项>"
+
+#: t/helper/test-path-walk.c
+msgid "toggle inclusion of blob objects"
+msgstr "切换是否包含数据对象"
+
+#: t/helper/test-path-walk.c
+msgid "toggle inclusion of commit objects"
+msgstr "切换是否包含提交对象"
+
+#: t/helper/test-path-walk.c
+msgid "toggle inclusion of tag objects"
+msgstr "切换是否包含标签对象"
+
+#: t/helper/test-path-walk.c
+msgid "toggle inclusion of tree objects"
+msgstr "切换是否包含树对象"
+
+#: t/helper/test-path-walk.c
+msgid "toggle pruning of uninteresting paths"
+msgstr "切换对无趣路径的修剪"
+
+#: t/helper/test-path-walk.c
+msgid "read a pattern list over stdin"
+msgstr "从标准输入读取模式列表"
+
#: t/helper/test-reach.c
#, c-format
msgid "commit %s is not marked reachable"
@@ -27703,6 +27898,11 @@ msgstr "错误:"
msgid "warning: "
msgstr "警告:"
+#: version.c
+#, c-format
+msgid "uname() failed with error '%s' (%d)\n"
+msgstr "uname() 失败,错误为 '%s'(%d)\n"
+
#: walker.c
msgid "Fetching objects"
msgstr "正在获取对象"
diff --git a/po/zh_TW.po b/po/zh_TW.po
index a61f544304..aa74d6537a 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -30,8 +30,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Git\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2024-12-28 13:16+0800\n"
-"PO-Revision-Date: 2024-12-28 13:23+0800\n"
+"POT-Creation-Date: 2025-03-09 10:39+0800\n"
+"PO-Revision-Date: 2025-03-09 10:52+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"
@@ -2841,6 +2841,22 @@ msgstr "git archive:通訊協定錯誤"
msgid "git archive: expected a flush"
msgstr "git archive:預期收到 flush 封包"
+#: builtin/backfill.c
+msgid "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+msgstr "git backfill [--min-batch-size=<n>] [--[no-]sparse]"
+
+#: builtin/backfill.c
+msgid "problem loading sparse-checkout"
+msgstr "載入稀疏簽出時發生問題"
+
+#: builtin/backfill.c
+msgid "Minimum number of objects to request at a time"
+msgstr "一次請求的最小物件數量"
+
+#: builtin/backfill.c
+msgid "Restrict the missing objects to the current sparse-checkout"
+msgstr "將缺少的物件限制於目前的稀疏簽出"
+
#: builtin/bisect.c
msgid ""
"git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>] [--no-"
@@ -2872,22 +2888,22 @@ msgstr "git bisect run <cmd> [<arg>...]"
#: builtin/bisect.c
#, c-format
msgid "cannot open file '%s' in mode '%s'"
-msgstr "無法以「%2$s」模式開啟「%1$s」檔案"
+msgstr "無法以「%2$s」模式開啟檔案「%1$s」"
#: builtin/bisect.c
#, c-format
msgid "could not write to file '%s'"
-msgstr "無法寫入「%s」檔案"
+msgstr "無法寫入檔案「%s」"
#: builtin/bisect.c
#, c-format
msgid "cannot open file '%s' for reading"
-msgstr "無法開啟「%s」檔案進行讀取"
+msgstr "無法開啟檔案「%s」來讀取"
#: builtin/bisect.c
#, c-format
msgid "'%s' is not a valid term"
-msgstr "「%s」不是有效術語"
+msgstr "「%s」不是有效的術語"
#: builtin/bisect.c
#, c-format
@@ -2917,7 +2933,7 @@ msgstr "「%s」不是有效的提交"
#, c-format
msgid ""
"could not check out original HEAD '%s'. Try 'git bisect reset <commit>'."
-msgstr "無法簽出原始 HEAD「%s」。請嘗試「git bisect reset <commit>」。"
+msgstr "無法簽出原本的 HEAD「%s」。請嘗試「git bisect reset <commit>」。"
#: builtin/bisect.c
#, c-format
@@ -2937,7 +2953,7 @@ msgstr "無法開啟檔案「%s」"
#: builtin/bisect.c
#, c-format
msgid "Invalid command: you're currently in a %s/%s bisect"
-msgstr "命令無效:您目前正處於二分搜尋 %s/%s 的狀態"
+msgstr "命令無效:您正處於二分搜尋 %s/%s 的狀態"
#: builtin/bisect.c
#, c-format
@@ -3263,7 +3279,7 @@ msgstr "顯示作者信箱而非名稱(預設值:off)"
msgid "ignore whitespace differences"
msgstr "忽略空白差異"
-#: builtin/blame.c builtin/log.c
+#: builtin/blame.c builtin/clone.c builtin/log.c
msgid "rev"
msgstr "rev"
@@ -3776,11 +3792,6 @@ msgid "git version:\n"
msgstr "git 版本:\n"
#: builtin/bugreport.c
-#, c-format
-msgid "uname() failed with error '%s' (%d)\n"
-msgstr "uname() 失敗,錯誤:「%s」(%d)\n"
-
-#: builtin/bugreport.c
msgid "compiler info: "
msgstr "編譯器資訊: "
@@ -5010,8 +5021,112 @@ msgid "clean.requireForce is true and -f not given: refusing to clean"
msgstr "clean.requireForce 是 true 且未給定 -f 選項:拒絕清理"
#: builtin/clone.c
-msgid "git clone [<options>] [--] <repo> [<dir>]"
-msgstr "git clone [<options>] [--] <repo> [<dir>]"
+#, c-format
+msgid "info: Could not add alternate for '%s': %s\n"
+msgstr "info: 不能為「%s」新增一個備用:%s\n"
+
+#: builtin/clone.c builtin/diff.c builtin/rm.c grep.c setup.c
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "對「%s」呼叫 stat 失敗"
+
+#: builtin/clone.c
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s 存在且不是目錄"
+
+#: builtin/clone.c
+#, c-format
+msgid "'%s' is a symlink, refusing to clone with --local"
+msgstr "「%s」是符號連結,故不能使用 --local 複製"
+
+#: builtin/clone.c
+#, c-format
+msgid "failed to start iterator over '%s'"
+msgstr "無法在「%s」上啟動迭代器"
+
+#: builtin/clone.c
+#, c-format
+msgid "symlink '%s' exists, refusing to clone with --local"
+msgstr "符號連結「%s」已存在,拒絕使用 --local 複製"
+
+#: builtin/clone.c compat/precompose_utf8.c
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "無法刪除「%s」"
+
+#: builtin/clone.c
+#, c-format
+msgid "hardlink cannot be checked at '%s'"
+msgstr "無法檢查位於「%s」的硬連結"
+
+#: builtin/clone.c
+#, c-format
+msgid "hardlink different from source at '%s'"
+msgstr "硬連結與位於「%s」的來源不同"
+
+#: builtin/clone.c
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "建立連結「%s」失敗"
+
+#: builtin/clone.c
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "複製檔案至「%s」失敗"
+
+#: builtin/clone.c refs/files-backend.c
+#, c-format
+msgid "failed to iterate over '%s'"
+msgstr "無法在「%s」上迭代"
+
+#: builtin/clone.c
+#, c-format
+msgid "done.\n"
+msgstr "完成。\n"
+
+#: builtin/clone.c
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry with 'git restore --source=HEAD :/'\n"
+msgstr ""
+"複製成功,但是簽出失敗。\n"
+"您可以透過「git status」檢查哪些已被簽出,然後使用指令\n"
+"「git restore --source=HEAD :/」重試\n"
+
+#: builtin/clone.c fetch-pack.c
+msgid "remote did not send all necessary objects"
+msgstr "遠端沒有傳送所有必需的物件"
+
+#: builtin/clone.c
+#, c-format
+msgid "unable to update %s"
+msgstr "不能更新 %s"
+
+#: builtin/clone.c
+msgid "failed to initialize sparse-checkout"
+msgstr "無法初始化稀疏簽出"
+
+#: builtin/clone.c
+msgid "remote HEAD refers to nonexistent ref, unable to checkout"
+msgstr "遠端 HEAD 指向不存在的引用,無法簽出"
+
+#: builtin/clone.c
+msgid "unable to checkout working tree"
+msgstr "不能簽出工作區"
+
+#: builtin/clone.c
+msgid "unable to write parameters to config file"
+msgstr "無法將參數寫入組態檔案"
+
+#: builtin/clone.c
+msgid "cannot repack to clean up"
+msgstr "無法執行 repack 來清理"
+
+#: builtin/clone.c
+msgid "cannot unlink temporary alternates file"
+msgstr "無法刪除暫存 alternates 檔案"
#: builtin/clone.c
msgid "don't clone shallow repository"
@@ -5084,6 +5199,10 @@ msgid "checkout <branch> instead of the remote's HEAD"
msgstr "簽出 <branch> 而不是遠端 HEAD"
#: builtin/clone.c
+msgid "clone single revision <rev> and check out"
+msgstr "複製單個修訂版 <rev> 並簽出"
+
+#: builtin/clone.c
msgid "path to git-upload-pack on the remote"
msgstr "遠端 git-upload-pack 路徑"
@@ -5112,8 +5231,8 @@ msgid "clone only one branch, HEAD or --branch"
msgstr "只複製一個分支、HEAD 或 --branch"
#: builtin/clone.c
-msgid "don't clone any tags, and make later fetches not to follow them"
-msgstr "不要複製任何標籤,之後取得也不要追蹤這些標籤"
+msgid "clone tags, and make later fetches not to follow them"
+msgstr "複製標籤,並使後續抓取不要追蹤這些標籤"
#: builtin/clone.c
msgid "any cloned submodules will be shallow"
@@ -5170,117 +5289,8 @@ msgid "a URI for downloading bundles before fetching from origin remote"
msgstr "在從 origin 遠端抓取前,用來下載套件包的 URI"
#: builtin/clone.c
-#, c-format
-msgid "info: Could not add alternate for '%s': %s\n"
-msgstr "info: 不能為 '%s' 新增一個備用:%s\n"
-
-#: builtin/clone.c builtin/diff.c builtin/rm.c grep.c setup.c
-#, c-format
-msgid "failed to stat '%s'"
-msgstr "對 '%s' 呼叫 stat 失敗"
-
-#: builtin/clone.c
-#, c-format
-msgid "%s exists and is not a directory"
-msgstr "%s 存在且不是一個目錄"
-
-#: builtin/clone.c
-#, c-format
-msgid "'%s' is a symlink, refusing to clone with --local"
-msgstr "「%s」是個符號連結,故不能使用 --local 複製"
-
-#: builtin/clone.c
-#, c-format
-msgid "failed to start iterator over '%s'"
-msgstr "無法在 '%s' 上啟動疊代器"
-
-#: builtin/clone.c
-#, c-format
-msgid "symlink '%s' exists, refusing to clone with --local"
-msgstr "「%s」符號連結已存在,拒絕使用 --local 複製"
-
-#: builtin/clone.c compat/precompose_utf8.c
-#, c-format
-msgid "failed to unlink '%s'"
-msgstr "無法刪除「%s」"
-
-#: builtin/clone.c
-#, c-format
-msgid "hardlink cannot be checked at '%s'"
-msgstr "無法檢查位於「%s」的硬連結"
-
-#: builtin/clone.c
-#, c-format
-msgid "hardlink different from source at '%s'"
-msgstr "硬連結與位於 '%s' 的來源不同"
-
-#: builtin/clone.c
-#, c-format
-msgid "failed to create link '%s'"
-msgstr "建立連結 '%s' 失敗"
-
-#: builtin/clone.c
-#, c-format
-msgid "failed to copy file to '%s'"
-msgstr "複製檔案至 '%s' 失敗"
-
-#: builtin/clone.c refs/files-backend.c
-#, c-format
-msgid "failed to iterate over '%s'"
-msgstr "無法在 '%s' 上疊代"
-
-#: builtin/clone.c
-#, c-format
-msgid "done.\n"
-msgstr "完成。\n"
-
-#: builtin/clone.c
-msgid ""
-"Clone succeeded, but checkout failed.\n"
-"You can inspect what was checked out with 'git status'\n"
-"and retry with 'git restore --source=HEAD :/'\n"
-msgstr ""
-"複製成功,但是簽出失敗。\n"
-"您可以透過 'git status' 檢查哪些已被簽出,然後使用指令\n"
-"'git restore --source=HEAD :/' 重試\n"
-
-#: builtin/clone.c
-#, c-format
-msgid "Could not find remote branch %s to clone."
-msgstr "找不到要複製的遠端分支 %s。"
-
-#: builtin/clone.c fetch-pack.c
-msgid "remote did not send all necessary objects"
-msgstr "遠端沒有傳送所有必需的物件"
-
-#: builtin/clone.c
-#, c-format
-msgid "unable to update %s"
-msgstr "不能更新 %s"
-
-#: builtin/clone.c
-msgid "failed to initialize sparse-checkout"
-msgstr "無法初始化稀疏簽出"
-
-#: builtin/clone.c
-msgid "remote HEAD refers to nonexistent ref, unable to checkout"
-msgstr "遠端 HEAD 指向一個不存在的引用,無法簽出"
-
-#: builtin/clone.c
-msgid "unable to checkout working tree"
-msgstr "不能簽出工作區"
-
-#: builtin/clone.c
-msgid "unable to write parameters to config file"
-msgstr "無法將參數寫入設定檔案"
-
-#: builtin/clone.c
-msgid "cannot repack to clean up"
-msgstr "無法執行 repack 來清理"
-
-#: builtin/clone.c
-msgid "cannot unlink temporary alternates file"
-msgstr "無法刪除暫存 alternates 檔案"
+msgid "git clone [<options>] [--] <repo> [<dir>]"
+msgstr "git clone [<options>] [--] <repo> [<dir>]"
#: builtin/clone.c
msgid "Too many arguments."
@@ -5404,7 +5414,12 @@ msgstr "遠端傳輸回報錯誤"
#: builtin/clone.c
#, c-format
msgid "Remote branch %s not found in upstream %s"
-msgstr "遠端分支 %s 在上游 %s 未發現"
+msgstr "上游 %2$s 上找不到遠端分支 %1$s"
+
+#: builtin/clone.c
+#, c-format
+msgid "Remote revision %s not found in upstream %s"
+msgstr "上游 %2$s 上找不到遠端修訂版 %1$s"
#: builtin/clone.c
msgid "You appear to have cloned an empty repository."
@@ -5469,7 +5484,8 @@ msgstr ""
"[no-]progress]\n"
" <split-options>"
-#: builtin/commit-graph.c builtin/fetch.c builtin/log.c builtin/repack.c
+#: builtin/commit-graph.c builtin/fetch.c builtin/gc.c builtin/log.c
+#: builtin/repack.c
msgid "dir"
msgstr "目錄"
@@ -5626,8 +5642,19 @@ msgid "git commit-tree: failed to read"
msgstr "git commit-tree:讀取失敗"
#: builtin/commit.c
-msgid ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+#| msgid ""
+#| "git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+#| " [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
+#| "reword):]<commit>]\n"
+#| " [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
+#| " [--allow-empty-message] [--no-verify] [-e] [--"
+#| "author=<author>]\n"
+#| " [--date=<date>] [--cleanup=<mode>] [--[no-]status]\n"
+#| " [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
+#| " [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
+#| " [--] [<pathspec>...]"
+msgid ""
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
"reword):]<commit>]\n"
" [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
@@ -5637,7 +5664,7 @@ msgid ""
" [(--trailer <token>[(=|:)<value>])...] [-S[<keyid>]]\n"
" [--] [<pathspec>...]"
msgstr ""
-"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n"
+"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n"
" [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|"
"reword):]<commit>]\n"
" [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n"
@@ -7358,14 +7385,14 @@ msgid ""
"Run 'git remote set-head %s %s' to follow the change, or set\n"
"'remote.%s.followRemoteHEAD' configuration option to a different value\n"
"if you do not want to see this message. Specifically running\n"
-"'git config set remote.%s.followRemoteHEAD %s' will disable the warning\n"
-"until the remote changes HEAD to something else."
+"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n"
+"will disable the warning until the remote changes HEAD to something else."
msgstr ""
-"執行「git remote set-head %s %s」以追蹤這個變更,或者\n"
-"如果您不想看到這則訊息,請將「remote.%s.followRemoteHEAD」\n"
-"組態選項設定成不同的值。更具體些來說,執行\n"
-"「git config set remote.%s.followRemoteHEAD %s」會停用這個警告,\n"
-"直到遠端將 HEAD 變更為其他內容。"
+"執行「git remote set-head %s %s」來跟進此差異,或者\n"
+"如果您不想看到這則訊息,請將「remote.%s.followRemoteHEAD」組態選項\n"
+"設定成別的值。更具體些來說,執行\n"
+"「git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s」\n"
+"會停用這個警告,直到遠端將 HEAD 改為指向其他東西。"
#: builtin/fetch.c
msgid "multiple branches detected, incompatible with --set-upstream"
@@ -8216,6 +8243,10 @@ msgstr "強制執行 gc 即使另外一個 gc 正在執行"
msgid "repack all other packs except the largest pack"
msgstr "除了最大的包之外,對所有其它包重新打包"
+#: builtin/gc.c builtin/repack.c
+msgid "pack prefix to store a pack containing pruned objects"
+msgstr "將前綴打包並儲存為包含已剪除物件的包"
+
#: builtin/gc.c
#, c-format
msgid "failed to parse gc.logExpiry value %s"
@@ -9209,6 +9240,11 @@ msgstr "無法結束 pack-objects 來重新封包"
msgid "Cannot come back to cwd"
msgstr "無法返回目前工作目錄"
+#: builtin/index-pack.c builtin/unpack-objects.c
+#, c-format
+msgid "bad --pack_header: %s"
+msgstr "無效的 --pack_header:%s"
+
#: builtin/index-pack.c
#, c-format
msgid "bad %s"
@@ -10311,11 +10347,6 @@ msgstr "未知的策略選項:-X%s"
msgid "malformed input line: '%s'."
msgstr "格式錯誤的輸入行:'%s'。"
-#: builtin/merge-tree.c
-#, c-format
-msgid "merging cannot continue; got unclean result of %d"
-msgstr "無法繼續合併:從 %d 收到的結果不乾淨"
-
#: builtin/merge.c
msgid "git merge [<options>] [<commit>...]"
msgstr "git merge [<選項>] [<提交>...]"
@@ -11334,6 +11365,15 @@ msgstr "git pack-objects [<選項>] <前綴名稱> [< <引用列表> | < <物件
#: builtin/pack-objects.c
#, c-format
+msgid "invalid --name-hash-version option: %d"
+msgstr "無效的 --name-hash-version 選項:%d"
+
+#: builtin/pack-objects.c
+msgid "currently, --write-bitmap-index requires --name-hash-version=1"
+msgstr "目前 --write-bitmap-index 需要指定 --name-hash-version=1"
+
+#: builtin/pack-objects.c
+#, c-format
msgid ""
"write_reuse_object: could not locate %s, expected at offset %<PRIuMAX> in "
"pack %s"
@@ -11468,8 +11508,8 @@ msgid ""
"value of uploadpack.blobpackfileuri must be of the form '<object-hash> <pack-"
"hash> <uri>' (got '%s')"
msgstr ""
-"uploadpack.blobpackfileuri 的值格式必須為 '<object-hash> <pack-hash> "
-"<uri>' (收到 '%s')"
+"uploadpack.blobpackfileuri 的值格式必須為「<object-hash> <pack-hash> <uri>」"
+"(收到「%s」)"
#: builtin/pack-objects.c
#, c-format
@@ -11740,6 +11780,10 @@ msgid "exclude any configured uploadpack.blobpackfileuri with this protocol"
msgstr "排除任何設定過,使用此通訊協定的 uploadpack.blobpackfileuri"
#: builtin/pack-objects.c
+msgid "use the specified name-hash function to group similar objects"
+msgstr "使用指定的名稱雜湊函式來為相似物件分組"
+
+#: builtin/pack-objects.c
#, c-format
msgid "delta chain depth %d is too deep, forcing %d"
msgstr "增量鏈深度 %d 太深了,強制為 %d"
@@ -13178,8 +13222,9 @@ msgid "invalid ref format: %s"
msgstr "無效的引用格式:%s"
#: builtin/refs.c
-msgid "git refs migrate --ref-format=<format> [--dry-run]"
-msgstr "git refs migrate --ref-format=<格式> [--dry-run]"
+#| msgid "git refs migrate --ref-format=<format> [--dry-run]"
+msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
+msgstr "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]"
#: builtin/refs.c
msgid "git refs verify [--strict] [--verbose]"
@@ -13194,6 +13239,10 @@ msgid "perform a non-destructive dry-run"
msgstr "進行非破壞性的試執行"
#: builtin/refs.c
+msgid "drop reflogs entirely during the migration"
+msgstr "在遷移過程中完全拋棄 reflog"
+
+#: builtin/refs.c
msgid "missing --ref-format=<format>"
msgstr "缺少 --ref-format=<格式>"
@@ -13764,8 +13813,14 @@ msgid "be verbose; must be placed before a subcommand"
msgstr "詳細輸出;必須置於子指令之前"
#: builtin/repack.c
-msgid "git repack [<options>]"
-msgstr "git repack [<選項>]"
+msgid ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
+msgstr ""
+"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n"
+"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n"
+"[--write-midx] [--name-hash-version=<n>]"
#: builtin/repack.c
msgid ""
@@ -13858,6 +13913,11 @@ msgid "pass --no-reuse-object to git-pack-objects"
msgstr "向 git-pack-objects 傳遞參數 --no-reuse-object"
#: builtin/repack.c
+msgid ""
+"specify the name hash version to use for grouping similar objects by path"
+msgstr "指定要用來以路徑為相似物件分組的名稱雜湊版本"
+
+#: builtin/repack.c
msgid "do not run git-update-server-info"
msgstr "不執行 git-update-server-info"
@@ -13922,10 +13982,6 @@ msgid "write a multi-pack index of the resulting packs"
msgstr "寫入結果包的多包索引"
#: builtin/repack.c
-msgid "pack prefix to store a pack containing pruned objects"
-msgstr "封裝前綴,儲存為包含過時物件的套件包"
-
-#: builtin/repack.c
msgid "pack prefix to store a pack containing filtered out objects"
msgstr "將前綴進行包裝,儲存為包含已過濾物件的封裝"
@@ -14198,10 +14254,6 @@ 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 "提供的所有正向修訂集必須為引用"
@@ -17424,6 +17476,10 @@ msgid "Create an archive of files from a named tree"
msgstr "基於命名過的樹建立檔案封存"
#: command-list.h
+msgid "Download missing objects in a partial clone"
+msgstr "在部分複製中下載缺少的物件"
+
+#: command-list.h
msgid "Use binary search to find the commit that introduced a bug"
msgstr "透過二分搜尋定位引入 bug 的提交"
@@ -18378,8 +18434,8 @@ msgstr "嘗試寫入提交圖,但「core.commitGraph」已停用"
#: commit-graph.c
#, c-format
msgid ""
-"attempting to write a commit-graph, but 'commitGraph."
-"changedPathsVersion' (%d) is not supported"
+"attempting to write a commit-graph, but 'commitGraph.changedPathsVersion' "
+"(%d) is not supported"
msgstr "嘗試寫入提交圖,但不支援「commitGraph.changedPathsVersion」(%d)"
#: commit-graph.c
@@ -19791,6 +19847,14 @@ msgid "invalid regex given to -I: '%s'"
msgstr "傳入 -I 的常規表示式無效:「%s」"
#: diff.c
+msgid "-G requires a non-empty argument"
+msgstr "-G 需要非空白引數"
+
+#: diff.c
+msgid "-S requires a non-empty argument"
+msgstr "-S 需要非空白引數"
+
+#: diff.c
#, c-format
msgid "failed to parse --submodule option parameter: '%s'"
msgstr "無法解析 --submodule 選項的參數:'%s'"
@@ -22419,6 +22483,11 @@ msgstr "無法寫檔案 %s"
#: object-file.c
#, c-format
+msgid "unable to write repeatedly vanishing file %s"
+msgstr "無法寫入重複消失的檔案 %s"
+
+#: object-file.c
+#, c-format
msgid "unable to set permission to '%s'"
msgstr "無法為 '%s' 設定權限"
@@ -23100,6 +23169,55 @@ msgstr "未知開關 `%c'"
msgid "unknown non-ascii option in string: `%s'"
msgstr "字串中未知的非 ascii 字元選項:`%s'"
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the long form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#: parse-options.c
+#, c-format
+msgid "[=<%s>]"
+msgstr "[=<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string
+#. stands for an optional value given to a command
+#. line option in the short form, and "<>" is there
+#. as a convention to signal that it is a
+#. placeholder (i.e. the user should substitute it
+#. with the real value). If your language uses a
+#. different convention, you can change "<%s>" part
+#. to match yours, e.g. it might use "|%s|" instead,
+#. or if the alphabet is different enough it may use
+#. "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#: parse-options.c
+#, c-format
+msgid "[<%s>]"
+msgstr "[<%s>]"
+
+#. TRANSLATORS: The "<%s>" part of this string stands for a
+#. value given to a command line option, and "<>" is there
+#. as a convention to signal that it is a placeholder
+#. (i.e. the user should substitute it with the real value).
+#. If your language uses a different convention, you can
+#. change "<%s>" part to match yours, e.g. it might use
+#. "|%s|" instead, or if the alphabet is different enough it
+#. may use "%s" without any placeholder signal. Most
+#. translations leave this message as is.
+#.
+#: parse-options.c
+#, c-format
+msgid " <%s>"
+msgstr " <%s>"
+
#: parse-options.c
msgid "..."
msgstr "..."
@@ -23203,6 +23321,25 @@ msgstr "「%2$s」的「%1$s」布林環境值無效"
msgid "failed to parse %s"
msgstr "解析 %s 失敗"
+#: path-walk.c
+#, c-format
+msgid "failed to walk children of tree %s: not found"
+msgstr "無法走訪樹 %s 的子代:找不到"
+
+#: path-walk.c
+#, c-format
+msgid "failed to find object %s"
+msgstr "找不到物件 %s"
+
+#: path-walk.c
+#, c-format
+msgid "failed to find tag %s"
+msgstr "找不到標籤 %s"
+
+#: path-walk.c
+msgid "failed to setup revision walk"
+msgstr "無法設置修訂版走訪"
+
#: path.c
#, c-format
msgid "Could not make %s writable by group"
@@ -23384,6 +23521,26 @@ msgstr "promisor 遠端名稱不能以 '/' 開始:%s"
msgid "could not fetch %s from promisor remote"
msgstr "無法從承諾者遠端抓取 %s"
+#: promisor-remote.c
+#, c-format
+msgid "known remote named '%s' but with url '%s' instead of '%s'"
+msgstr "已知有遠端名為「%s」,但其 URL 為「%s」而非「%s」"
+
+#: promisor-remote.c
+#, c-format
+msgid "unknown '%s' value for '%s' config option"
+msgstr "「%2$s」組態選項的值「%1$s」未知"
+
+#: promisor-remote.c
+#, c-format
+msgid "unknown element '%s' from remote info"
+msgstr "遠端資訊的元素「%s」未知"
+
+#: promisor-remote.c
+#, c-format
+msgid "accepted promisor remote '%s' not found"
+msgstr "找不到接受的承諾者遠端「%s」"
+
#: protocol-caps.c
msgid "object-info: expected flush after arguments"
msgstr "object-info:引數後預期要有 flush"
@@ -24351,6 +24508,16 @@ msgstr "引用名稱 %s 是符號引用,不支援複製"
msgid "invalid refspec '%s'"
msgstr "無效的引用規格:「%s」"
+#: refspec.c
+#, c-format
+msgid "pattern '%s' has no '*'"
+msgstr "符合模式「%s」中沒有「*」"
+
+#: refspec.c
+#, c-format
+msgid "replacement '%s' has no '*'"
+msgstr "取代文字「%s」中沒有「*」"
+
#: remote-curl.c
#, c-format
msgid "invalid quoting in push-option value: '%s'"
@@ -24504,6 +24671,28 @@ msgstr "remote-curl:未知的來自 git 的指令 '%s'"
#: remote.c
#, c-format
+msgid ""
+"reading remote from \"%s/%s\", which is nominated for removal.\n"
+"\n"
+"If you still use the \"remotes/\" directory it is recommended to\n"
+"migrate to config-based remotes:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"If you cannot, please let us know why you still need to use it by\n"
+"sending an e-mail to <git@vger.kernel.org>."
+msgstr ""
+"正在自「%s/%s」讀取遠端,該路徑已被標記為即將移除。\n"
+"\n"
+"如果您仍在使用「remotes/」目錄,建議您遷移至基於組態的遠端:\n"
+"\n"
+"\tgit remote rename %s %s\n"
+"\n"
+"如果您無法遷移,請告訴我們您為何仍需使用此功能,\n"
+"請寄信至 <git@vger.kernel.org>。"
+
+#: remote.c
+#, c-format
msgid "config remote shorthand cannot begin with '/': %s"
msgstr "設定的遠端短名稱不能以 '/' 開始:%s"
@@ -24547,16 +24736,6 @@ msgstr "%s 同時追蹤 %s 和 %s"
#: remote.c
#, c-format
-msgid "key '%s' of pattern had no '*'"
-msgstr "模式的鍵 '%s' 沒有 '*'"
-
-#: remote.c
-#, c-format
-msgid "value '%s' of pattern has no '*'"
-msgstr "模式的值 '%s' 沒有 '*'"
-
-#: remote.c
-#, c-format
msgid "src refspec %s does not match any"
msgstr "來源引用規格 %s 沒有符合項目"
@@ -26824,6 +27003,34 @@ msgstr "每次迭代前清除快取樹狀物件"
msgid "number of entries in the cache tree to invalidate (default 0)"
msgstr "在快取樹狀物件中,要使失效的項目數量(預設值為 0)"
+#: t/helper/test-path-walk.c
+msgid "test-tool path-walk <options> -- <revision-options>"
+msgstr "test-tool path-walk <options> -- <revision-options>"
+
+#: t/helper/test-path-walk.c
+msgid "toggle inclusion of blob objects"
+msgstr "切換是否包含資料物件"
+
+#: t/helper/test-path-walk.c
+msgid "toggle inclusion of commit objects"
+msgstr "切換是否包含提交物件"
+
+#: t/helper/test-path-walk.c
+msgid "toggle inclusion of tag objects"
+msgstr "切換是否包含標籤物件"
+
+#: t/helper/test-path-walk.c
+msgid "toggle inclusion of tree objects"
+msgstr "切換是否包含樹狀物件"
+
+#: t/helper/test-path-walk.c
+msgid "toggle pruning of uninteresting paths"
+msgstr "切換是否剪除不重要路徑"
+
+#: t/helper/test-path-walk.c
+msgid "read a pattern list over stdin"
+msgstr "從標準輸入讀取模式清單"
+
#: t/helper/test-reach.c
#, c-format
msgid "commit %s is not marked reachable"
@@ -27571,6 +27778,11 @@ msgstr "錯誤: "
msgid "warning: "
msgstr "警告: "
+#: version.c
+#, c-format
+msgid "uname() failed with error '%s' (%d)\n"
+msgstr "uname() 失敗,錯誤:「%s」(%d)\n"
+
#: walker.c
msgid "Fetching objects"
msgstr "正在抓取物件"
@@ -28758,6 +28970,24 @@ msgid "Do you really want to send %s? [y|N]: "
msgstr "您真的要傳送 %s?[y|N]: "
#, c-format
+#~ msgid "Could not find remote branch %s to clone."
+#~ msgstr "找不到要複製的遠端分支 %s。"
+
+#, c-format
+#~ msgid "merging cannot continue; got unclean result of %d"
+#~ msgstr "無法繼續合併:從 %d 收到的結果不乾淨"
+
+#~ msgid "git repack [<options>]"
+#~ msgstr "git repack [<選項>]"
+
+#~ msgid "--onto and --advance are incompatible"
+#~ msgstr "--onto 和 --advance 不相容"
+
+#, c-format
+#~ msgid "key '%s' of pattern had no '*'"
+#~ msgstr "模式的鍵 '%s' 沒有 '*'"
+
+#, c-format
#~ msgid "preferred pack (%s) is invalid"
#~ msgstr "偏好的封包 (%s) 無效"
diff --git a/promisor-remote.c b/promisor-remote.c
index c714f4f007..5801ebfd9b 100644
--- a/promisor-remote.c
+++ b/promisor-remote.c
@@ -11,6 +11,8 @@
#include "strvec.h"
#include "packfile.h"
#include "environment.h"
+#include "url.h"
+#include "version.h"
struct promisor_remote_config {
struct promisor_remote *promisors;
@@ -221,6 +223,18 @@ int repo_has_promisor_remote(struct repository *r)
return !!repo_promisor_remote_find(r, NULL);
}
+int repo_has_accepted_promisor_remote(struct repository *r)
+{
+ struct promisor_remote *p;
+
+ promisor_remote_init(r);
+
+ for (p = r->promisor_remote_config->promisors; p; p = p->next)
+ if (p->accepted)
+ return 1;
+ return 0;
+}
+
static int remove_fetched_oids(struct repository *repo,
struct object_id **oids,
int oid_nr, int to_free)
@@ -292,3 +306,236 @@ all_fetched:
if (to_free)
free(remaining_oids);
}
+
+static int allow_unsanitized(char ch)
+{
+ if (ch == ',' || ch == ';' || ch == '%')
+ return 0;
+ return ch > 32 && ch < 127;
+}
+
+static void promisor_info_vecs(struct repository *repo,
+ struct strvec *names,
+ struct strvec *urls)
+{
+ struct promisor_remote *r;
+
+ promisor_remote_init(repo);
+
+ for (r = repo->promisor_remote_config->promisors; r; r = r->next) {
+ const char *url;
+ char *url_key = xstrfmt("remote.%s.url", r->name);
+
+ /* Only add remotes with a non empty URL */
+ if (!git_config_get_string_tmp(url_key, &url) && *url) {
+ strvec_push(names, r->name);
+ strvec_push(urls, url);
+ }
+
+ free(url_key);
+ }
+}
+
+char *promisor_remote_info(struct repository *repo)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int advertise_promisors = 0;
+ struct strvec names = STRVEC_INIT;
+ struct strvec urls = STRVEC_INIT;
+
+ git_config_get_bool("promisor.advertise", &advertise_promisors);
+
+ if (!advertise_promisors)
+ return NULL;
+
+ promisor_info_vecs(repo, &names, &urls);
+
+ if (!names.nr)
+ return NULL;
+
+ for (size_t i = 0; i < names.nr; i++) {
+ if (i)
+ strbuf_addch(&sb, ';');
+ strbuf_addstr(&sb, "name=");
+ strbuf_addstr_urlencode(&sb, names.v[i], allow_unsanitized);
+ strbuf_addstr(&sb, ",url=");
+ strbuf_addstr_urlencode(&sb, urls.v[i], allow_unsanitized);
+ }
+
+ strvec_clear(&names);
+ strvec_clear(&urls);
+
+ return strbuf_detach(&sb, NULL);
+}
+
+/*
+ * Find first index of 'nicks' where there is 'nick'. 'nick' is
+ * compared case sensitively to the strings in 'nicks'. If not found
+ * 'nicks->nr' is returned.
+ */
+static size_t remote_nick_find(struct strvec *nicks, const char *nick)
+{
+ for (size_t i = 0; i < nicks->nr; i++)
+ if (!strcmp(nicks->v[i], nick))
+ return i;
+ return nicks->nr;
+}
+
+enum accept_promisor {
+ ACCEPT_NONE = 0,
+ ACCEPT_KNOWN_URL,
+ ACCEPT_KNOWN_NAME,
+ ACCEPT_ALL
+};
+
+static int should_accept_remote(enum accept_promisor accept,
+ const char *remote_name, const char *remote_url,
+ struct strvec *names, struct strvec *urls)
+{
+ size_t i;
+
+ if (accept == ACCEPT_ALL)
+ return 1;
+
+ i = remote_nick_find(names, remote_name);
+
+ if (i >= names->nr)
+ /* We don't know about that remote */
+ return 0;
+
+ if (accept == ACCEPT_KNOWN_NAME)
+ return 1;
+
+ if (accept != ACCEPT_KNOWN_URL)
+ BUG("Unhandled 'enum accept_promisor' value '%d'", accept);
+
+ if (!remote_url || !*remote_url) {
+ warning(_("no or empty URL advertised for remote '%s'"), remote_name);
+ return 0;
+ }
+
+ if (!strcmp(urls->v[i], remote_url))
+ return 1;
+
+ warning(_("known remote named '%s' but with URL '%s' instead of '%s'"),
+ remote_name, urls->v[i], remote_url);
+
+ return 0;
+}
+
+static void filter_promisor_remote(struct repository *repo,
+ struct strvec *accepted,
+ const char *info)
+{
+ struct strbuf **remotes;
+ const char *accept_str;
+ enum accept_promisor accept = ACCEPT_NONE;
+ struct strvec names = STRVEC_INIT;
+ struct strvec urls = STRVEC_INIT;
+
+ if (!git_config_get_string_tmp("promisor.acceptfromserver", &accept_str)) {
+ if (!*accept_str || !strcasecmp("None", accept_str))
+ accept = ACCEPT_NONE;
+ else if (!strcasecmp("KnownUrl", accept_str))
+ accept = ACCEPT_KNOWN_URL;
+ else if (!strcasecmp("KnownName", accept_str))
+ accept = ACCEPT_KNOWN_NAME;
+ else if (!strcasecmp("All", accept_str))
+ accept = ACCEPT_ALL;
+ else
+ warning(_("unknown '%s' value for '%s' config option"),
+ accept_str, "promisor.acceptfromserver");
+ }
+
+ if (accept == ACCEPT_NONE)
+ return;
+
+ if (accept != ACCEPT_ALL)
+ promisor_info_vecs(repo, &names, &urls);
+
+ /* Parse remote info received */
+
+ remotes = strbuf_split_str(info, ';', 0);
+
+ for (size_t i = 0; remotes[i]; i++) {
+ struct strbuf **elems;
+ const char *remote_name = NULL;
+ const char *remote_url = NULL;
+ char *decoded_name = NULL;
+ char *decoded_url = NULL;
+
+ strbuf_strip_suffix(remotes[i], ";");
+ elems = strbuf_split(remotes[i], ',');
+
+ for (size_t j = 0; elems[j]; j++) {
+ int res;
+ strbuf_strip_suffix(elems[j], ",");
+ res = skip_prefix(elems[j]->buf, "name=", &remote_name) ||
+ skip_prefix(elems[j]->buf, "url=", &remote_url);
+ if (!res)
+ warning(_("unknown element '%s' from remote info"),
+ elems[j]->buf);
+ }
+
+ if (remote_name)
+ decoded_name = url_percent_decode(remote_name);
+ if (remote_url)
+ decoded_url = url_percent_decode(remote_url);
+
+ if (decoded_name && should_accept_remote(accept, decoded_name, decoded_url, &names, &urls))
+ strvec_push(accepted, decoded_name);
+
+ strbuf_list_free(elems);
+ free(decoded_name);
+ free(decoded_url);
+ }
+
+ strvec_clear(&names);
+ strvec_clear(&urls);
+ strbuf_list_free(remotes);
+}
+
+char *promisor_remote_reply(const char *info)
+{
+ struct strvec accepted = STRVEC_INIT;
+ struct strbuf reply = STRBUF_INIT;
+
+ filter_promisor_remote(the_repository, &accepted, info);
+
+ if (!accepted.nr)
+ return NULL;
+
+ for (size_t i = 0; i < accepted.nr; i++) {
+ if (i)
+ strbuf_addch(&reply, ';');
+ strbuf_addstr_urlencode(&reply, accepted.v[i], allow_unsanitized);
+ }
+
+ strvec_clear(&accepted);
+
+ return strbuf_detach(&reply, NULL);
+}
+
+void mark_promisor_remotes_as_accepted(struct repository *r, const char *remotes)
+{
+ struct strbuf **accepted_remotes = strbuf_split_str(remotes, ';', 0);
+
+ for (size_t i = 0; accepted_remotes[i]; i++) {
+ struct promisor_remote *p;
+ char *decoded_remote;
+
+ strbuf_strip_suffix(accepted_remotes[i], ";");
+ decoded_remote = url_percent_decode(accepted_remotes[i]->buf);
+
+ p = repo_promisor_remote_find(r, decoded_remote);
+ if (p)
+ p->accepted = 1;
+ else
+ warning(_("accepted promisor remote '%s' not found"),
+ decoded_remote);
+
+ free(decoded_remote);
+ }
+
+ strbuf_list_free(accepted_remotes);
+}
diff --git a/promisor-remote.h b/promisor-remote.h
index 88cb599c39..263d331a55 100644
--- a/promisor-remote.h
+++ b/promisor-remote.h
@@ -9,11 +9,13 @@ struct object_id;
* Promisor remote linked list
*
* Information in its fields come from remote.XXX config entries or
- * from extensions.partialclone.
+ * from extensions.partialclone, except for 'accepted' which comes
+ * from protocol v2 capabilities exchange.
*/
struct promisor_remote {
struct promisor_remote *next;
char *partial_clone_filter;
+ unsigned int accepted : 1;
const char name[FLEX_ARRAY];
};
@@ -32,4 +34,37 @@ void promisor_remote_get_direct(struct repository *repo,
const struct object_id *oids,
int oid_nr);
+/*
+ * Prepare a "promisor-remote" advertisement by a server.
+ * Check the value of "promisor.advertise" and maybe the configured
+ * promisor remotes, if any, to prepare information to send in an
+ * advertisement.
+ * Return value is NULL if no promisor remote advertisement should be
+ * made. Otherwise it contains the names and urls of the advertised
+ * promisor remotes separated by ';'. See gitprotocol-v2(5).
+ */
+char *promisor_remote_info(struct repository *repo);
+
+/*
+ * Prepare a reply to a "promisor-remote" advertisement from a server.
+ * Check the value of "promisor.acceptfromserver" and maybe the
+ * configured promisor remotes, if any, to prepare the reply.
+ * Return value is NULL if no promisor remote from the server
+ * is accepted. Otherwise it contains the names of the accepted promisor
+ * remotes separated by ';'. See gitprotocol-v2(5).
+ */
+char *promisor_remote_reply(const char *info);
+
+/*
+ * Set the 'accepted' flag for some promisor remotes. Useful on the
+ * server side when some promisor remotes have been accepted by the
+ * client.
+ */
+void mark_promisor_remotes_as_accepted(struct repository *repo, const char *remotes);
+
+/*
+ * Has any promisor remote been accepted by the client?
+ */
+int repo_has_accepted_promisor_remote(struct repository *r);
+
#endif /* PROMISOR_REMOTE_H */
diff --git a/pseudo-merge.h b/pseudo-merge.h
index 29df8a32ec..cf0e62ecd1 100644
--- a/pseudo-merge.h
+++ b/pseudo-merge.h
@@ -101,7 +101,7 @@ void select_pseudo_merges(struct bitmap_writer *writer);
/*
* Represents a serialized view of a file containing pseudo-merge(s)
- * (see Documentation/technical/bitmap-format.txt for a specification
+ * (see Documentation/technical/bitmap-format.adoc for a specification
* of the format).
*/
struct pseudo_merge_map {
@@ -210,7 +210,7 @@ int cascade_pseudo_merges(const struct pseudo_merge_map *pm,
/*
* Returns a pseudo-merge which contains the exact set of commits
- * listed in the "parents" bitamp, or NULL if none could be found.
+ * listed in the "parents" bitmap, or NULL if none could be found.
*/
struct pseudo_merge *pseudo_merge_for_parents(const struct pseudo_merge_map *pm,
struct bitmap *parents);
diff --git a/reachable.c b/reachable.c
index ecf7ccf504..9ee04c89ec 100644
--- a/reachable.c
+++ b/reachable.c
@@ -65,8 +65,10 @@ static void add_rebase_files(struct rev_info *revs)
struct worktree **worktrees = get_worktrees();
for (struct worktree **wt = worktrees; *wt; wt++) {
+ char *wt_gitdir = get_worktree_git_dir(*wt);
+
strbuf_reset(&buf);
- strbuf_addstr(&buf, get_worktree_git_dir(*wt));
+ strbuf_addstr(&buf, wt_gitdir);
strbuf_complete(&buf, '/');
len = buf.len;
for (size_t i = 0; i < ARRAY_SIZE(path); i++) {
@@ -74,6 +76,8 @@ static void add_rebase_files(struct rev_info *revs)
strbuf_addstr(&buf, path[i]);
add_one_file(buf.buf, revs);
}
+
+ free(wt_gitdir);
}
strbuf_release(&buf);
free_worktrees(worktrees);
diff --git a/read-cache.c b/read-cache.c
index 7ef01c3806..e678c13e8f 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -3251,15 +3251,18 @@ static int clean_shared_index_files(const char *current_hex)
while ((de = readdir(dir)) != NULL) {
const char *sha1_hex;
- const char *shared_index_path;
+ char *shared_index_path;
if (!skip_prefix(de->d_name, "sharedindex.", &sha1_hex))
continue;
if (!strcmp(sha1_hex, current_hex))
continue;
- shared_index_path = git_path("%s", de->d_name);
+
+ shared_index_path = repo_git_path(the_repository, "%s", de->d_name);
if (should_delete_shared_index(shared_index_path) > 0 &&
unlink(shared_index_path))
warning_errno(_("unable to unlink: %s"), shared_index_path);
+
+ free(shared_index_path);
}
closedir(dir);
@@ -3271,6 +3274,7 @@ static int write_shared_index(struct index_state *istate,
{
struct split_index *si = istate->split_index;
int ret, was_full = !istate->sparse_index;
+ char *path;
move_cache_to_base_index(istate);
convert_to_sparse(istate, 0);
@@ -3286,18 +3290,20 @@ static int write_shared_index(struct index_state *istate,
if (ret)
return ret;
- ret = adjust_shared_perm(get_tempfile_path(*temp));
+ ret = adjust_shared_perm(the_repository, get_tempfile_path(*temp));
if (ret) {
error(_("cannot fix permission bits on '%s'"), get_tempfile_path(*temp));
return ret;
}
- ret = rename_tempfile(temp,
- git_path("sharedindex.%s", oid_to_hex(&si->base->oid)));
+
+ path = repo_git_path(the_repository, "sharedindex.%s", oid_to_hex(&si->base->oid));
+ ret = rename_tempfile(temp, path);
if (!ret) {
oidcpy(&si->base_oid, &si->base->oid);
clean_shared_index_files(oid_to_hex(&si->base->oid));
}
+ free(path);
return ret;
}
@@ -3378,9 +3384,12 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
if (new_shared_index) {
struct tempfile *temp;
int saved_errno;
+ char *path;
/* Same initial permissions as the main .git/index file */
- temp = mks_tempfile_sm(git_path("sharedindex_XXXXXX"), 0, 0666);
+ path = repo_git_path(the_repository, "sharedindex_XXXXXX");
+ temp = mks_tempfile_sm(path, 0, 0666);
+ free(path);
if (!temp) {
ret = do_write_locked_index(istate, lock, flags,
~WRITE_SPLIT_INDEX_EXTENSION);
@@ -3401,9 +3410,10 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
/* Freshen the shared index only if the split-index was written */
if (!ret && !new_shared_index && !is_null_oid(&si->base_oid)) {
- const char *shared_index = git_path("sharedindex.%s",
- oid_to_hex(&si->base_oid));
+ char *shared_index = repo_git_path(the_repository, "sharedindex.%s",
+ oid_to_hex(&si->base_oid));
freshen_shared_index(shared_index, 1);
+ free(shared_index);
}
out:
diff --git a/refs.c b/refs.c
index 91da5325d7..f0fe77bd7c 100644
--- a/refs.c
+++ b/refs.c
@@ -1699,6 +1699,24 @@ struct ref_iterator *refs_ref_iterator_begin(
enum do_for_each_ref_flags flags)
{
struct ref_iterator *iter;
+ struct strvec normalized_exclude_patterns = STRVEC_INIT;
+
+ if (exclude_patterns) {
+ for (size_t i = 0; exclude_patterns[i]; i++) {
+ const char *pattern = exclude_patterns[i];
+ size_t len = strlen(pattern);
+ if (!len)
+ continue;
+
+ if (pattern[len - 1] == '/')
+ strvec_push(&normalized_exclude_patterns, pattern);
+ else
+ strvec_pushf(&normalized_exclude_patterns, "%s/",
+ pattern);
+ }
+
+ exclude_patterns = normalized_exclude_patterns.v;
+ }
if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
static int ref_paranoia = -1;
@@ -1719,6 +1737,8 @@ struct ref_iterator *refs_ref_iterator_begin(
if (trim)
iter = prefix_ref_iterator_begin(iter, "", trim);
+ strvec_clear(&normalized_exclude_patterns);
+
return iter;
}
@@ -2154,7 +2174,7 @@ struct ref_store *repo_get_submodule_ref_store(struct repository *repo,
if (!is_nonbare_repository_dir(&submodule_sb))
goto done;
- if (submodule_to_gitdir(&submodule_sb, submodule))
+ if (submodule_to_gitdir(repo, &submodule_sb, submodule))
goto done;
subrepo = xmalloc(sizeof(*subrepo));
@@ -2192,8 +2212,8 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)
if (wt->id) {
struct strbuf common_path = STRBUF_INIT;
- strbuf_git_common_path(&common_path, wt->repo,
- "worktrees/%s", wt->id);
+ repo_common_path_append(wt->repo, &common_path,
+ "worktrees/%s", wt->id);
refs = ref_store_init(wt->repo, wt->repo->ref_storage_format,
common_path.buf, REF_STORE_ALL_CAPS);
strbuf_release(&common_path);
@@ -2475,19 +2495,18 @@ int ref_transaction_commit(struct ref_transaction *transaction,
return ret;
}
-int refs_verify_refname_available(struct ref_store *refs,
- const char *refname,
- const struct string_list *extras,
- const struct string_list *skip,
- unsigned int initial_transaction,
- struct strbuf *err)
+int refs_verify_refnames_available(struct ref_store *refs,
+ const struct string_list *refnames,
+ const struct string_list *extras,
+ const struct string_list *skip,
+ unsigned int initial_transaction,
+ struct strbuf *err)
{
- const char *slash;
- const char *extra_refname;
struct strbuf dirname = STRBUF_INIT;
struct strbuf referent = STRBUF_INIT;
- struct object_id oid;
- unsigned int type;
+ struct string_list_item *item;
+ struct ref_iterator *iter = NULL;
+ struct strset dirnames;
int ret = -1;
/*
@@ -2497,86 +2516,130 @@ int refs_verify_refname_available(struct ref_store *refs,
assert(err);
- strbuf_grow(&dirname, strlen(refname) + 1);
- for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
- /*
- * Just saying "Is a directory" when we e.g. can't
- * lock some multi-level ref isn't very informative,
- * the user won't be told *what* is a directory, so
- * let's not use strerror() below.
- */
- int ignore_errno;
- /* Expand dirname to the new prefix, not including the trailing slash: */
- strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
+ strset_init(&dirnames);
+
+ for_each_string_list_item(item, refnames) {
+ const char *refname = item->string;
+ const char *extra_refname;
+ struct object_id oid;
+ unsigned int type;
+ const char *slash;
+
+ strbuf_reset(&dirname);
+
+ for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
+ /*
+ * Just saying "Is a directory" when we e.g. can't
+ * lock some multi-level ref isn't very informative,
+ * the user won't be told *what* is a directory, so
+ * let's not use strerror() below.
+ */
+ int ignore_errno;
+
+ /* Expand dirname to the new prefix, not including the trailing slash: */
+ strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
+
+ /*
+ * We are still at a leading dir of the refname (e.g.,
+ * "refs/foo"; if there is a reference with that name,
+ * it is a conflict, *unless* it is in skip.
+ */
+ if (skip && string_list_has_string(skip, dirname.buf))
+ continue;
+
+ /*
+ * If we've already seen the directory we don't need to
+ * process it again. Skip it to avoid checking checking
+ * common prefixes like "refs/heads/" repeatedly.
+ */
+ if (!strset_add(&dirnames, dirname.buf))
+ continue;
+
+ if (!initial_transaction &&
+ !refs_read_raw_ref(refs, dirname.buf, &oid, &referent,
+ &type, &ignore_errno)) {
+ strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
+ dirname.buf, refname);
+ goto cleanup;
+ }
+
+ if (extras && string_list_has_string(extras, dirname.buf)) {
+ strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
+ refname, dirname.buf);
+ goto cleanup;
+ }
+ }
/*
- * We are still at a leading dir of the refname (e.g.,
- * "refs/foo"; if there is a reference with that name,
- * it is a conflict, *unless* it is in skip.
+ * We are at the leaf of our refname (e.g., "refs/foo/bar").
+ * There is no point in searching for a reference with that
+ * name, because a refname isn't considered to conflict with
+ * itself. But we still need to check for references whose
+ * names are in the "refs/foo/bar/" namespace, because they
+ * *do* conflict.
*/
- if (skip && string_list_has_string(skip, dirname.buf))
- continue;
+ strbuf_addstr(&dirname, refname + dirname.len);
+ strbuf_addch(&dirname, '/');
- if (!initial_transaction &&
- !refs_read_raw_ref(refs, dirname.buf, &oid, &referent,
- &type, &ignore_errno)) {
- strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
- dirname.buf, refname);
- goto cleanup;
- }
+ if (!initial_transaction) {
+ int ok;
- if (extras && string_list_has_string(extras, dirname.buf)) {
- strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
- refname, dirname.buf);
- goto cleanup;
- }
- }
+ if (!iter) {
+ iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
+ DO_FOR_EACH_INCLUDE_BROKEN);
+ } else if (ref_iterator_seek(iter, dirname.buf) < 0) {
+ goto cleanup;
+ }
- /*
- * We are at the leaf of our refname (e.g., "refs/foo/bar").
- * There is no point in searching for a reference with that
- * name, because a refname isn't considered to conflict with
- * itself. But we still need to check for references whose
- * names are in the "refs/foo/bar/" namespace, because they
- * *do* conflict.
- */
- strbuf_addstr(&dirname, refname + dirname.len);
- strbuf_addch(&dirname, '/');
-
- if (!initial_transaction) {
- struct ref_iterator *iter;
- int ok;
-
- iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0,
- DO_FOR_EACH_INCLUDE_BROKEN);
- while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- if (skip &&
- string_list_has_string(skip, iter->refname))
- continue;
+ while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
+ if (skip &&
+ string_list_has_string(skip, iter->refname))
+ continue;
- strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
- iter->refname, refname);
- ref_iterator_abort(iter);
- goto cleanup;
+ strbuf_addf(err, _("'%s' exists; cannot create '%s'"),
+ iter->refname, refname);
+ goto cleanup;
+ }
+
+ if (ok != ITER_DONE)
+ BUG("error while iterating over references");
}
- if (ok != ITER_DONE)
- BUG("error while iterating over references");
+ extra_refname = find_descendant_ref(dirname.buf, extras, skip);
+ if (extra_refname) {
+ strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
+ refname, extra_refname);
+ goto cleanup;
+ }
}
- extra_refname = find_descendant_ref(dirname.buf, extras, skip);
- if (extra_refname)
- strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"),
- refname, extra_refname);
- else
- ret = 0;
+ ret = 0;
cleanup:
strbuf_release(&referent);
strbuf_release(&dirname);
+ strset_clear(&dirnames);
+ ref_iterator_free(iter);
return ret;
}
+int refs_verify_refname_available(struct ref_store *refs,
+ const char *refname,
+ const struct string_list *extras,
+ const struct string_list *skip,
+ unsigned int initial_transaction,
+ struct strbuf *err)
+{
+ struct string_list_item item = { .string = (char *) refname };
+ struct string_list refnames = {
+ .items = &item,
+ .nr = 1,
+ };
+
+ return refs_verify_refnames_available(refs, &refnames, extras, skip,
+ initial_transaction, err);
+}
+
struct do_for_each_reflog_help {
each_reflog_fn *fn;
void *cb_data;
diff --git a/refs.h b/refs.h
index 2ed14fdb16..240e2d8537 100644
--- a/refs.h
+++ b/refs.h
@@ -124,6 +124,18 @@ int refs_verify_refname_available(struct ref_store *refs,
unsigned int initial_transaction,
struct strbuf *err);
+/*
+ * Same as `refs_verify_refname_available()`, but checking for a list of
+ * refnames instead of only a single item. This is more efficient in the case
+ * where one needs to check multiple refnames.
+ */
+int refs_verify_refnames_available(struct ref_store *refs,
+ const struct string_list *refnames,
+ const struct string_list *extras,
+ const struct string_list *skip,
+ unsigned int initial_transaction,
+ struct strbuf *err);
+
int refs_ref_exists(struct ref_store *refs, const char *refname);
int should_autocreate_reflog(enum log_refs_config log_all_ref_updates,
@@ -577,7 +589,7 @@ int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_dat
/*
* Return 0 iff refname has the correct format for a refname according
- * to the rules described in Documentation/git-check-ref-format.txt.
+ * to the rules described in Documentation/git-check-ref-format.adoc.
* If REFNAME_ALLOW_ONELEVEL is set in flags, then accept one-level
* reference names. If REFNAME_REFSPEC_PATTERN is set in flags, then
* allow a single "*" wildcard character in the refspec. No leading or
diff --git a/refs/debug.c b/refs/debug.c
index fbc4df08b4..5390fa9c18 100644
--- a/refs/debug.c
+++ b/refs/debug.c
@@ -169,6 +169,16 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
return res;
}
+static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct debug_ref_iterator *diter =
+ (struct debug_ref_iterator *)ref_iterator;
+ int res = diter->iter->vtable->seek(diter->iter, prefix);
+ trace_printf_key(&trace_refs, "iterator_seek: %s: %d\n", prefix ? prefix : "", res);
+ return res;
+}
+
static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -179,19 +189,19 @@ static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
return res;
}
-static int debug_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void debug_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct debug_ref_iterator *diter =
(struct debug_ref_iterator *)ref_iterator;
- int res = diter->iter->vtable->abort(diter->iter);
- trace_printf_key(&trace_refs, "iterator_abort: %d\n", res);
- return res;
+ diter->iter->vtable->release(diter->iter);
+ trace_printf_key(&trace_refs, "iterator_abort\n");
}
static struct ref_iterator_vtable debug_ref_iterator_vtable = {
.advance = debug_ref_iterator_advance,
+ .seek = debug_ref_iterator_seek,
.peel = debug_ref_iterator_peel,
- .abort = debug_ref_iterator_abort,
+ .release = debug_ref_iterator_release,
};
static struct ref_iterator *
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 29f08dced4..ff54a4bb7e 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -678,6 +678,7 @@ static void unlock_ref(struct ref_lock *lock)
*/
static int lock_raw_ref(struct files_ref_store *refs,
const char *refname, int mustexist,
+ struct string_list *refnames_to_check,
const struct string_list *extras,
struct ref_lock **lock_p,
struct strbuf *referent,
@@ -855,16 +856,11 @@ retry:
}
/*
- * If the ref did not exist and we are creating it,
- * make sure there is no existing packed ref that
- * conflicts with refname:
+ * If the ref did not exist and we are creating it, we have to
+ * make sure there is no existing packed ref that conflicts
+ * with refname. This check is deferred so that we can batch it.
*/
- if (refs_verify_refname_available(
- refs->packed_ref_store, refname,
- extras, NULL, 0, err)) {
- ret = TRANSACTION_NAME_CONFLICT;
- goto error_return;
- }
+ string_list_append(refnames_to_check, refname);
}
ret = 0;
@@ -919,13 +915,17 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
return ITER_OK;
}
- iter->iter0 = NULL;
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- ok = ITER_ERROR;
-
return ok;
}
+static int files_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct files_ref_iterator *iter =
+ (struct files_ref_iterator *)ref_iterator;
+ return ref_iterator_seek(iter->iter0, prefix);
+}
+
static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -935,23 +935,18 @@ static int files_ref_iterator_peel(struct ref_iterator *ref_iterator,
return ref_iterator_peel(iter->iter0, peeled);
}
-static int files_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void files_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_ref_iterator *iter =
(struct files_ref_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
- if (iter->iter0)
- ok = ref_iterator_abort(iter->iter0);
-
- base_ref_iterator_free(ref_iterator);
- return ok;
+ ref_iterator_free(iter->iter0);
}
static struct ref_iterator_vtable files_ref_iterator_vtable = {
.advance = files_ref_iterator_advance,
+ .seek = files_ref_iterator_seek,
.peel = files_ref_iterator_peel,
- .abort = files_ref_iterator_abort,
+ .release = files_ref_iterator_release,
};
static struct ref_iterator *files_ref_iterator_begin(
@@ -1382,7 +1377,7 @@ static int should_pack_refs(struct files_ref_store *refs,
iter->flags, opts))
refcount++;
if (refcount >= limit) {
- ref_iterator_abort(iter);
+ ref_iterator_free(iter);
return 1;
}
}
@@ -1390,6 +1385,7 @@ static int should_pack_refs(struct files_ref_store *refs,
if (ret != ITER_DONE)
die("error while iterating over references");
+ ref_iterator_free(iter);
return 0;
}
@@ -1456,6 +1452,7 @@ static int files_pack_refs(struct ref_store *ref_store,
packed_refs_unlock(refs->packed_ref_store);
prune_refs(refs, &refs_to_prune);
+ ref_iterator_free(iter);
strbuf_release(&err);
return 0;
}
@@ -1831,7 +1828,7 @@ static int log_ref_setup(struct files_ref_store *refs,
}
if (*logfd >= 0)
- adjust_shared_perm(logfile);
+ adjust_shared_perm(the_repository, logfile);
free(logfile);
return 0;
@@ -2303,35 +2300,33 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
return ITER_OK;
}
- iter->dir_iterator = NULL;
- if (ref_iterator_abort(ref_iterator) == ITER_ERROR)
- ok = ITER_ERROR;
return ok;
}
+static int files_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
+ const char *prefix UNUSED)
+{
+ BUG("ref_iterator_seek() called for reflog_iterator");
+}
+
static int files_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
struct object_id *peeled UNUSED)
{
BUG("ref_iterator_peel() called for reflog_iterator");
}
-static int files_reflog_iterator_abort(struct ref_iterator *ref_iterator)
+static void files_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct files_reflog_iterator *iter =
(struct files_reflog_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
- if (iter->dir_iterator)
- ok = dir_iterator_abort(iter->dir_iterator);
-
- base_ref_iterator_free(ref_iterator);
- return ok;
+ dir_iterator_free(iter->dir_iterator);
}
static struct ref_iterator_vtable files_reflog_iterator_vtable = {
.advance = files_reflog_iterator_advance,
+ .seek = files_reflog_iterator_seek,
.peel = files_reflog_iterator_peel,
- .abort = files_reflog_iterator_abort,
+ .release = files_reflog_iterator_release,
};
static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
@@ -2569,6 +2564,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
struct ref_update *update,
struct ref_transaction *transaction,
const char *head_ref,
+ struct string_list *refnames_to_check,
struct string_list *affected_refnames,
struct strbuf *err)
{
@@ -2597,7 +2593,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
lock->count++;
} else {
ret = lock_raw_ref(refs, update->refname, mustexist,
- affected_refnames,
+ refnames_to_check, affected_refnames,
&lock, &referent,
&update->type, err);
if (ret) {
@@ -2811,6 +2807,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
size_t i;
int ret = 0;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+ struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
char *head_ref = NULL;
int head_type;
struct files_transaction_backend_data *backend_data;
@@ -2898,7 +2895,8 @@ static int files_transaction_prepare(struct ref_store *ref_store,
struct ref_update *update = transaction->updates[i];
ret = lock_ref_for_update(refs, update, transaction,
- head_ref, &affected_refnames, err);
+ head_ref, &refnames_to_check,
+ &affected_refnames, err);
if (ret)
goto cleanup;
@@ -2930,6 +2928,26 @@ static int files_transaction_prepare(struct ref_store *ref_store,
}
}
+ /*
+ * Verify that none of the loose reference that we're about to write
+ * conflict with any existing packed references. Ideally, we'd do this
+ * check after the packed-refs are locked so that the file cannot
+ * change underneath our feet. But introducing such a lock now would
+ * probably do more harm than good as users rely on there not being a
+ * global lock with the "files" backend.
+ *
+ * Another alternative would be to do the check after the (optional)
+ * lock, but that would extend the time we spend in the globally-locked
+ * state.
+ *
+ * So instead, we accept the race for now.
+ */
+ if (refs_verify_refnames_available(refs->packed_ref_store, &refnames_to_check,
+ &affected_refnames, NULL, 0, err)) {
+ ret = TRANSACTION_NAME_CONFLICT;
+ goto cleanup;
+ }
+
if (packed_transaction) {
if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
ret = TRANSACTION_GENERIC_ERROR;
@@ -2972,6 +2990,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
cleanup:
free(head_ref);
string_list_clear(&affected_refnames, 0);
+ string_list_clear(&refnames_to_check, 0);
if (ret)
files_transaction_cleanup(refs, transaction);
@@ -3036,6 +3055,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
size_t i;
int ret = 0;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+ struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
struct ref_transaction *packed_transaction = NULL;
struct ref_transaction *loose_transaction = NULL;
@@ -3085,11 +3105,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
!is_null_oid(&update->old_oid))
BUG("initial ref transaction with old_sha1 set");
- if (refs_verify_refname_available(&refs->base, update->refname,
- &affected_refnames, NULL, 1, err)) {
- ret = TRANSACTION_NAME_CONFLICT;
- goto cleanup;
- }
+ string_list_append(&refnames_to_check, update->refname);
/*
* packed-refs don't support symbolic refs, root refs and reflogs,
@@ -3125,8 +3141,19 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
}
}
- if (packed_refs_lock(refs->packed_ref_store, 0, err) ||
- ref_transaction_commit(packed_transaction, err)) {
+ if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+
+ if (refs_verify_refnames_available(&refs->base, &refnames_to_check,
+ &affected_refnames, NULL, 1, err)) {
+ packed_refs_unlock(refs->packed_ref_store);
+ ret = TRANSACTION_NAME_CONFLICT;
+ goto cleanup;
+ }
+
+ if (ref_transaction_commit(packed_transaction, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
@@ -3147,6 +3174,7 @@ cleanup:
ref_transaction_free(packed_transaction);
transaction->state = REF_TRANSACTION_CLOSED;
string_list_clear(&affected_refnames, 0);
+ string_list_clear(&refnames_to_check, 0);
return ret;
}
@@ -3488,8 +3516,8 @@ static int files_ref_store_create_on_disk(struct ref_store *ref_store,
* they do not understand the reference format extension.
*/
strbuf_addf(&sb, "%s/refs", ref_store->gitdir);
- safe_create_dir(sb.buf, 1);
- adjust_shared_perm(sb.buf);
+ safe_create_dir(the_repository, sb.buf, 1);
+ adjust_shared_perm(the_repository, sb.buf);
/*
* There is no need to create directories for common refs when creating
@@ -3501,11 +3529,11 @@ static int files_ref_store_create_on_disk(struct ref_store *ref_store,
*/
strbuf_reset(&sb);
files_ref_path(refs, &sb, "refs/heads");
- safe_create_dir(sb.buf, 1);
+ safe_create_dir(the_repository, sb.buf, 1);
strbuf_reset(&sb);
files_ref_path(refs, &sb, "refs/tags");
- safe_create_dir(sb.buf, 1);
+ safe_create_dir(the_repository, sb.buf, 1);
}
strbuf_release(&sb);
@@ -3808,6 +3836,7 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
ret = error(_("failed to iterate over '%s'"), sb.buf);
out:
+ dir_iterator_free(iter);
strbuf_release(&sb);
strbuf_release(&refname);
return ret;
diff --git a/refs/iterator.c b/refs/iterator.c
index d25e568bf0..766d96e795 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -15,15 +15,26 @@ int ref_iterator_advance(struct ref_iterator *ref_iterator)
return ref_iterator->vtable->advance(ref_iterator);
}
+int ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ return ref_iterator->vtable->seek(ref_iterator, prefix);
+}
+
int ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
return ref_iterator->vtable->peel(ref_iterator, peeled);
}
-int ref_iterator_abort(struct ref_iterator *ref_iterator)
+void ref_iterator_free(struct ref_iterator *ref_iterator)
{
- return ref_iterator->vtable->abort(ref_iterator);
+ if (ref_iterator) {
+ ref_iterator->vtable->release(ref_iterator);
+ /* Help make use-after-free bugs fail quickly: */
+ ref_iterator->vtable = NULL;
+ free(ref_iterator);
+ }
}
void base_ref_iterator_init(struct ref_iterator *iter,
@@ -36,20 +47,19 @@ void base_ref_iterator_init(struct ref_iterator *iter,
iter->flags = 0;
}
-void base_ref_iterator_free(struct ref_iterator *iter)
-{
- /* Help make use-after-free bugs fail quickly: */
- iter->vtable = NULL;
- free(iter);
-}
-
struct empty_ref_iterator {
struct ref_iterator base;
};
-static int empty_ref_iterator_advance(struct ref_iterator *ref_iterator)
+static int empty_ref_iterator_advance(struct ref_iterator *ref_iterator UNUSED)
+{
+ return ITER_DONE;
+}
+
+static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
+ const char *prefix UNUSED)
{
- return ref_iterator_abort(ref_iterator);
+ return 0;
}
static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
@@ -58,16 +68,15 @@ static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
BUG("peel called for empty iterator");
}
-static int empty_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void empty_ref_iterator_release(struct ref_iterator *ref_iterator UNUSED)
{
- base_ref_iterator_free(ref_iterator);
- return ITER_DONE;
}
static struct ref_iterator_vtable empty_ref_iterator_vtable = {
.advance = empty_ref_iterator_advance,
+ .seek = empty_ref_iterator_seek,
.peel = empty_ref_iterator_peel,
- .abort = empty_ref_iterator_abort,
+ .release = empty_ref_iterator_release,
};
struct ref_iterator *empty_ref_iterator_begin(void)
@@ -87,7 +96,8 @@ int is_empty_ref_iterator(struct ref_iterator *ref_iterator)
struct merge_ref_iterator {
struct ref_iterator base;
- struct ref_iterator *iter0, *iter1;
+ struct ref_iterator *iter0, *iter0_owned;
+ struct ref_iterator *iter1, *iter1_owned;
ref_iterator_select_fn *select;
void *cb_data;
@@ -179,9 +189,8 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
iter->select(iter->iter0, iter->iter1, iter->cb_data);
if (selection == ITER_SELECT_DONE) {
- return ref_iterator_abort(ref_iterator);
+ return ITER_DONE;
} else if (selection == ITER_SELECT_ERROR) {
- ref_iterator_abort(ref_iterator);
return ITER_ERROR;
}
@@ -211,10 +220,31 @@ static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
}
error:
- ref_iterator_abort(ref_iterator);
return ITER_ERROR;
}
+static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct merge_ref_iterator *iter =
+ (struct merge_ref_iterator *)ref_iterator;
+ int ret;
+
+ iter->current = NULL;
+ iter->iter0 = iter->iter0_owned;
+ iter->iter1 = iter->iter1_owned;
+
+ ret = ref_iterator_seek(iter->iter0, prefix);
+ if (ret < 0)
+ return ret;
+
+ ret = ref_iterator_seek(iter->iter1, prefix);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -227,28 +257,19 @@ static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
return ref_iterator_peel(*iter->current, peeled);
}
-static int merge_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void merge_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct merge_ref_iterator *iter =
(struct merge_ref_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
- if (iter->iter0) {
- if (ref_iterator_abort(iter->iter0) != ITER_DONE)
- ok = ITER_ERROR;
- }
- if (iter->iter1) {
- if (ref_iterator_abort(iter->iter1) != ITER_DONE)
- ok = ITER_ERROR;
- }
- base_ref_iterator_free(ref_iterator);
- return ok;
+ ref_iterator_free(iter->iter0_owned);
+ ref_iterator_free(iter->iter1_owned);
}
static struct ref_iterator_vtable merge_ref_iterator_vtable = {
.advance = merge_ref_iterator_advance,
+ .seek = merge_ref_iterator_seek,
.peel = merge_ref_iterator_peel,
- .abort = merge_ref_iterator_abort,
+ .release = merge_ref_iterator_release,
};
struct ref_iterator *merge_ref_iterator_begin(
@@ -267,8 +288,8 @@ struct ref_iterator *merge_ref_iterator_begin(
*/
base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable);
- iter->iter0 = iter0;
- iter->iter1 = iter1;
+ iter->iter0 = iter->iter0_owned = iter0;
+ iter->iter1 = iter->iter1_owned = iter1;
iter->select = select;
iter->cb_data = cb_data;
iter->current = NULL;
@@ -310,10 +331,10 @@ struct ref_iterator *overlay_ref_iterator_begin(
* them.
*/
if (is_empty_ref_iterator(front)) {
- ref_iterator_abort(front);
+ ref_iterator_free(front);
return back;
} else if (is_empty_ref_iterator(back)) {
- ref_iterator_abort(back);
+ ref_iterator_free(back);
return front;
}
@@ -350,19 +371,15 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
int cmp = compare_prefix(iter->iter0->refname, iter->prefix);
-
if (cmp < 0)
continue;
-
- if (cmp > 0) {
- /*
- * As the source iterator is ordered, we
- * can stop the iteration as soon as we see a
- * refname that comes after the prefix:
- */
- ok = ref_iterator_abort(iter->iter0);
- break;
- }
+ /*
+ * As the source iterator is ordered, we
+ * can stop the iteration as soon as we see a
+ * refname that comes after the prefix:
+ */
+ if (cmp > 0)
+ return ITER_DONE;
if (iter->trim) {
/*
@@ -386,12 +403,19 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
return ITER_OK;
}
- iter->iter0 = NULL;
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- return ITER_ERROR;
return ok;
}
+static int prefix_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct prefix_ref_iterator *iter =
+ (struct prefix_ref_iterator *)ref_iterator;
+ free(iter->prefix);
+ iter->prefix = xstrdup_or_null(prefix);
+ return ref_iterator_seek(iter->iter0, prefix);
+}
+
static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -401,23 +425,19 @@ static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
return ref_iterator_peel(iter->iter0, peeled);
}
-static int prefix_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void prefix_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct prefix_ref_iterator *iter =
(struct prefix_ref_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
- if (iter->iter0)
- ok = ref_iterator_abort(iter->iter0);
+ ref_iterator_free(iter->iter0);
free(iter->prefix);
- base_ref_iterator_free(ref_iterator);
- return ok;
}
static struct ref_iterator_vtable prefix_ref_iterator_vtable = {
.advance = prefix_ref_iterator_advance,
+ .seek = prefix_ref_iterator_seek,
.peel = prefix_ref_iterator_peel,
- .abort = prefix_ref_iterator_abort,
+ .release = prefix_ref_iterator_release,
};
struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
@@ -453,20 +473,14 @@ int do_for_each_ref_iterator(struct ref_iterator *iter,
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
retval = fn(iter->refname, iter->referent, iter->oid, iter->flags, cb_data);
- if (retval) {
- /*
- * If ref_iterator_abort() returns ITER_ERROR,
- * we ignore that error in deference to the
- * callback function's return value.
- */
- ref_iterator_abort(iter);
+ if (retval)
goto out;
- }
}
out:
current_ref_iter = old_ref_iter;
if (ok == ITER_ERROR)
- return -1;
+ retval = -1;
+ ref_iterator_free(iter);
return retval;
}
diff --git a/refs/packed-backend.c b/refs/packed-backend.c
index a7b6f74b6e..b4289a7d9c 100644
--- a/refs/packed-backend.c
+++ b/refs/packed-backend.c
@@ -4,6 +4,7 @@
#include "../git-compat-util.h"
#include "../config.h"
#include "../dir.h"
+#include "../fsck.h"
#include "../gettext.h"
#include "../hash.h"
#include "../hex.h"
@@ -299,14 +300,9 @@ struct snapshot_record {
size_t len;
};
-static int cmp_packed_ref_records(const void *v1, const void *v2,
- void *cb_data)
-{
- const struct snapshot *snapshot = cb_data;
- const struct snapshot_record *e1 = v1, *e2 = v2;
- const char *r1 = e1->start + snapshot_hexsz(snapshot) + 1;
- const char *r2 = e2->start + snapshot_hexsz(snapshot) + 1;
+static int cmp_packed_refname(const char *r1, const char *r2)
+{
while (1) {
if (*r1 == '\n')
return *r2 == '\n' ? 0 : -1;
@@ -321,6 +317,17 @@ static int cmp_packed_ref_records(const void *v1, const void *v2,
}
}
+static int cmp_packed_ref_records(const void *v1, const void *v2,
+ void *cb_data)
+{
+ const struct snapshot *snapshot = cb_data;
+ const struct snapshot_record *e1 = v1, *e2 = v2;
+ const char *r1 = e1->start + snapshot_hexsz(snapshot) + 1;
+ const char *r2 = e2->start + snapshot_hexsz(snapshot) + 1;
+
+ return cmp_packed_refname(r1, r2);
+}
+
/*
* Compare a snapshot record at `rec` to the specified NUL-terminated
* refname.
@@ -493,6 +500,21 @@ static void verify_buffer_safe(struct snapshot *snapshot)
last_line, eof - last_line);
}
+/*
+ * When parsing the "packed-refs" file, we will parse it line by line.
+ * Because we know the start pointer of the refname and the next
+ * newline pointer, we could calculate the length of the refname by
+ * subtracting the two pointers. However, there is a corner case where
+ * the refname contains corrupted embedded NUL characters. And
+ * `check_refname_format()` will not catch this when the truncated
+ * refname is still a valid refname. To prevent this, we need to check
+ * whether the refname contains the NUL characters.
+ */
+static int refname_contains_nul(struct strbuf *refname)
+{
+ return !!memchr(refname->buf, '\0', refname->len);
+}
+
#define SMALL_FILE_SIZE (32*1024)
/*
@@ -693,7 +715,7 @@ static struct snapshot *create_snapshot(struct packed_ref_store *refs)
tmp = xmemdupz(snapshot->buf, eol - snapshot->buf);
- if (!skip_prefix(tmp, "# pack-refs with:", (const char **)&p))
+ if (!skip_prefix(tmp, "# pack-refs with: ", (const char **)&p))
die_invalid_line(refs->path,
snapshot->buf,
snapshot->eof - snapshot->buf);
@@ -819,6 +841,8 @@ struct packed_ref_iterator {
struct snapshot *snapshot;
+ char *prefix;
+
/* The current position in the snapshot's buffer: */
const char *pos;
@@ -841,11 +865,9 @@ struct packed_ref_iterator {
};
/*
- * Move the iterator to the next record in the snapshot, without
- * respect for whether the record is actually required by the current
- * iteration. Adjust the fields in `iter` and return `ITER_OK` or
- * `ITER_DONE`. This function does not free the iterator in the case
- * of `ITER_DONE`.
+ * Move the iterator to the next record in the snapshot. Adjust the fields in
+ * `iter` and return `ITER_OK` or `ITER_DONE`. This function does not free the
+ * iterator in the case of `ITER_DONE`.
*/
static int next_record(struct packed_ref_iterator *iter)
{
@@ -894,6 +916,9 @@ static int next_record(struct packed_ref_iterator *iter)
strbuf_add(&iter->refname_buf, p, eol - p);
iter->base.refname = iter->refname_buf.buf;
+ if (refname_contains_nul(&iter->refname_buf))
+ die("packed refname contains embedded NULL: %s", iter->base.refname);
+
if (check_refname_format(iter->base.refname, REFNAME_ALLOW_ONELEVEL)) {
if (!refname_is_safe(iter->base.refname))
die("packed refname is dangerous: %s",
@@ -942,6 +967,9 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
int ok;
while ((ok = next_record(iter)) == ITER_OK) {
+ const char *refname = iter->base.refname;
+ const char *prefix = iter->prefix;
+
if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
!is_per_worktree_ref(iter->base.refname))
continue;
@@ -951,15 +979,41 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator)
&iter->oid, iter->flags))
continue;
+ while (prefix && *prefix) {
+ if (*refname < *prefix)
+ BUG("packed-refs backend yielded reference preceding its prefix");
+ else if (*refname > *prefix)
+ return ITER_DONE;
+ prefix++;
+ refname++;
+ }
+
return ITER_OK;
}
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- ok = ITER_ERROR;
-
return ok;
}
+static int packed_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct packed_ref_iterator *iter =
+ (struct packed_ref_iterator *)ref_iterator;
+ const char *start;
+
+ if (prefix && *prefix)
+ start = find_reference_location(iter->snapshot, prefix, 0);
+ else
+ start = iter->snapshot->start;
+
+ free(iter->prefix);
+ iter->prefix = xstrdup_or_null(prefix);
+ iter->pos = start;
+ iter->eof = iter->snapshot->eof;
+
+ return 0;
+}
+
static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -976,23 +1030,21 @@ static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator,
}
}
-static int packed_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void packed_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct packed_ref_iterator *iter =
(struct packed_ref_iterator *)ref_iterator;
- int ok = ITER_DONE;
-
strbuf_release(&iter->refname_buf);
free(iter->jump);
+ free(iter->prefix);
release_snapshot(iter->snapshot);
- base_ref_iterator_free(ref_iterator);
- return ok;
}
static struct ref_iterator_vtable packed_ref_iterator_vtable = {
.advance = packed_ref_iterator_advance,
+ .seek = packed_ref_iterator_seek,
.peel = packed_ref_iterator_peel,
- .abort = packed_ref_iterator_abort
+ .release = packed_ref_iterator_release,
};
static int jump_list_entry_cmp(const void *va, const void *vb)
@@ -1104,7 +1156,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
{
struct packed_ref_store *refs;
struct snapshot *snapshot;
- const char *start;
struct packed_ref_iterator *iter;
struct ref_iterator *ref_iterator;
unsigned int required_flags = REF_STORE_READ;
@@ -1120,14 +1171,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
*/
snapshot = get_snapshot(refs);
- if (prefix && *prefix)
- start = find_reference_location(snapshot, prefix, 0);
- else
- start = snapshot->start;
-
- if (start == snapshot->eof)
- return empty_ref_iterator_begin();
-
CALLOC_ARRAY(iter, 1);
ref_iterator = &iter->base;
base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable);
@@ -1137,19 +1180,15 @@ static struct ref_iterator *packed_ref_iterator_begin(
iter->snapshot = snapshot;
acquire_snapshot(snapshot);
-
- iter->pos = start;
- iter->eof = snapshot->eof;
strbuf_init(&iter->refname_buf, 0);
-
iter->base.oid = &iter->oid;
-
iter->repo = ref_store->repo;
iter->flags = flags;
- if (prefix && *prefix)
- /* Stop iteration after we've gone *past* prefix: */
- ref_iterator = prefix_ref_iterator_begin(ref_iterator, prefix, 0);
+ if (packed_ref_iterator_seek(&iter->base, prefix) < 0) {
+ ref_iterator_free(&iter->base);
+ return NULL;
+ }
return ref_iterator;
}
@@ -1362,8 +1401,10 @@ static int write_with_updates(struct packed_ref_store *refs,
*/
iter = packed_ref_iterator_begin(&refs->base, "", NULL,
DO_FOR_EACH_INCLUDE_BROKEN);
- if ((ok = ref_iterator_advance(iter)) != ITER_OK)
+ if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
+ ref_iterator_free(iter);
iter = NULL;
+ }
i = 0;
@@ -1411,8 +1452,10 @@ static int write_with_updates(struct packed_ref_store *refs,
* the iterator over the unneeded
* value.
*/
- if ((ok = ref_iterator_advance(iter)) != ITER_OK)
+ if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
+ ref_iterator_free(iter);
iter = NULL;
+ }
cmp = +1;
} else {
/*
@@ -1449,8 +1492,10 @@ static int write_with_updates(struct packed_ref_store *refs,
peel_error ? NULL : &peeled))
goto write_error;
- if ((ok = ref_iterator_advance(iter)) != ITER_OK)
+ if ((ok = ref_iterator_advance(iter)) != ITER_OK) {
+ ref_iterator_free(iter);
iter = NULL;
+ }
} else if (is_null_oid(&update->new_oid)) {
/*
* The update wants to delete the reference,
@@ -1499,9 +1544,7 @@ write_error:
get_tempfile_path(refs->tempfile), strerror(errno));
error:
- if (iter)
- ref_iterator_abort(iter);
-
+ ref_iterator_free(iter);
delete_tempfile(&refs->tempfile);
return -1;
}
@@ -1748,15 +1791,329 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
return empty_ref_iterator_begin();
}
-static int packed_fsck(struct ref_store *ref_store UNUSED,
- struct fsck_options *o UNUSED,
+static int packed_fsck_ref_next_line(struct fsck_options *o,
+ unsigned long line_number, const char *start,
+ const char *eof, const char **eol)
+{
+ int ret = 0;
+
+ *eol = memchr(start, '\n', eof - start);
+ if (!*eol) {
+ struct strbuf packed_entry = STRBUF_INIT;
+ struct fsck_ref_report report = { 0 };
+
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_PACKED_REF_ENTRY_NOT_TERMINATED,
+ "'%.*s' is not terminated with a newline",
+ (int)(eof - start), start);
+
+ /*
+ * There is no newline but we still want to parse it to the end of
+ * the buffer.
+ */
+ *eol = eof;
+ strbuf_release(&packed_entry);
+ }
+
+ return ret;
+}
+
+static int packed_fsck_ref_header(struct fsck_options *o,
+ const char *start, const char *eol,
+ unsigned int *sorted)
+{
+ struct string_list traits = STRING_LIST_INIT_NODUP;
+ char *tmp_line;
+ int ret = 0;
+ char *p;
+
+ tmp_line = xmemdupz(start, eol - start);
+ if (!skip_prefix(tmp_line, "# pack-refs with: ", (const char **)&p)) {
+ struct fsck_ref_report report = { 0 };
+ report.path = "packed-refs.header";
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_PACKED_REF_HEADER,
+ "'%.*s' does not start with '# pack-refs with: '",
+ (int)(eol - start), start);
+ goto cleanup;
+ }
+
+ string_list_split_in_place(&traits, p, " ", -1);
+ *sorted = unsorted_string_list_has_string(&traits, "sorted");
+
+cleanup:
+ free(tmp_line);
+ string_list_clear(&traits, 0);
+ return ret;
+}
+
+static int packed_fsck_ref_peeled_line(struct fsck_options *o,
+ struct ref_store *ref_store,
+ unsigned long line_number,
+ const char *start, const char *eol)
+{
+ struct strbuf packed_entry = STRBUF_INIT;
+ struct fsck_ref_report report = { 0 };
+ struct object_id peeled;
+ const char *p;
+ int ret = 0;
+
+ /*
+ * Skip the '^' and parse the peeled oid.
+ */
+ start++;
+ if (parse_oid_hex_algop(start, &peeled, &p, ref_store->repo->hash_algo)) {
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_PACKED_REF_ENTRY,
+ "'%.*s' has invalid peeled oid",
+ (int)(eol - start), start);
+ goto cleanup;
+ }
+
+ if (p != eol) {
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_PACKED_REF_ENTRY,
+ "has trailing garbage after peeled oid '%.*s'",
+ (int)(eol - p), p);
+ goto cleanup;
+ }
+
+cleanup:
+ strbuf_release(&packed_entry);
+ return ret;
+}
+
+static int packed_fsck_ref_main_line(struct fsck_options *o,
+ struct ref_store *ref_store,
+ unsigned long line_number,
+ struct strbuf *refname,
+ const char *start, const char *eol)
+{
+ struct strbuf packed_entry = STRBUF_INIT;
+ struct fsck_ref_report report = { 0 };
+ struct object_id oid;
+ const char *p;
+ int ret = 0;
+
+ if (parse_oid_hex_algop(start, &oid, &p, ref_store->repo->hash_algo)) {
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_PACKED_REF_ENTRY,
+ "'%.*s' has invalid oid",
+ (int)(eol - start), start);
+ goto cleanup;
+ }
+
+ if (p == eol || !isspace(*p)) {
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_PACKED_REF_ENTRY,
+ "has no space after oid '%s' but with '%.*s'",
+ oid_to_hex(&oid), (int)(eol - p), p);
+ goto cleanup;
+ }
+
+ p++;
+ strbuf_reset(refname);
+ strbuf_add(refname, p, eol - p);
+ if (refname_contains_nul(refname)) {
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_PACKED_REF_ENTRY,
+ "refname '%s' contains NULL binaries",
+ refname->buf);
+ }
+
+ if (check_refname_format(refname->buf, 0)) {
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_REF_NAME,
+ "has bad refname '%s'", refname->buf);
+ }
+
+cleanup:
+ strbuf_release(&packed_entry);
+ return ret;
+}
+
+static int packed_fsck_ref_sorted(struct fsck_options *o,
+ struct ref_store *ref_store,
+ const char *start, const char *eof)
+{
+ size_t hexsz = ref_store->repo->hash_algo->hexsz;
+ struct strbuf packed_entry = STRBUF_INIT;
+ struct fsck_ref_report report = { 0 };
+ struct strbuf refname1 = STRBUF_INIT;
+ struct strbuf refname2 = STRBUF_INIT;
+ unsigned long line_number = 1;
+ const char *former = NULL;
+ const char *current;
+ const char *eol;
+ int ret = 0;
+
+ if (*start == '#') {
+ eol = memchr(start, '\n', eof - start);
+ start = eol + 1;
+ line_number++;
+ }
+
+ for (; start < eof; line_number++, start = eol + 1) {
+ eol = memchr(start, '\n', eof - start);
+
+ if (*start == '^')
+ continue;
+
+ if (!former) {
+ former = start + hexsz + 1;
+ continue;
+ }
+
+ current = start + hexsz + 1;
+ if (cmp_packed_refname(former, current) >= 0) {
+ const char *err_fmt =
+ "refname '%s' is less than previous refname '%s'";
+
+ eol = memchr(former, '\n', eof - former);
+ strbuf_add(&refname1, former, eol - former);
+ eol = memchr(current, '\n', eof - current);
+ strbuf_add(&refname2, current, eol - current);
+
+ strbuf_addf(&packed_entry, "packed-refs line %lu", line_number);
+ report.path = packed_entry.buf;
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_PACKED_REF_UNSORTED,
+ err_fmt, refname2.buf, refname1.buf);
+ goto cleanup;
+ }
+ former = current;
+ }
+
+cleanup:
+ strbuf_release(&packed_entry);
+ strbuf_release(&refname1);
+ strbuf_release(&refname2);
+ return ret;
+}
+
+static int packed_fsck_ref_content(struct fsck_options *o,
+ struct ref_store *ref_store,
+ unsigned int *sorted,
+ const char *start, const char *eof)
+{
+ struct strbuf refname = STRBUF_INIT;
+ unsigned long line_number = 1;
+ const char *eol;
+ int ret = 0;
+
+ ret |= packed_fsck_ref_next_line(o, line_number, start, eof, &eol);
+ if (*start == '#') {
+ ret |= packed_fsck_ref_header(o, start, eol, sorted);
+
+ start = eol + 1;
+ line_number++;
+ }
+
+ while (start < eof) {
+ ret |= packed_fsck_ref_next_line(o, line_number, start, eof, &eol);
+ ret |= packed_fsck_ref_main_line(o, ref_store, line_number, &refname, start, eol);
+ start = eol + 1;
+ line_number++;
+ if (start < eof && *start == '^') {
+ ret |= packed_fsck_ref_next_line(o, line_number, start, eof, &eol);
+ ret |= packed_fsck_ref_peeled_line(o, ref_store, line_number,
+ start, eol);
+ start = eol + 1;
+ line_number++;
+ }
+ }
+
+ strbuf_release(&refname);
+ return ret;
+}
+
+static int packed_fsck(struct ref_store *ref_store,
+ struct fsck_options *o,
struct worktree *wt)
{
+ struct packed_ref_store *refs = packed_downcast(ref_store,
+ REF_STORE_READ, "fsck");
+ struct strbuf packed_ref_content = STRBUF_INIT;
+ unsigned int sorted = 0;
+ struct stat st;
+ int ret = 0;
+ int fd = -1;
if (!is_main_worktree(wt))
- return 0;
+ goto cleanup;
- return 0;
+ if (o->verbose)
+ fprintf_ln(stderr, "Checking packed-refs file %s", refs->path);
+
+ fd = open_nofollow(refs->path, O_RDONLY);
+ if (fd < 0) {
+ /*
+ * If the packed-refs file doesn't exist, there's nothing
+ * to check.
+ */
+ if (errno == ENOENT)
+ goto cleanup;
+
+ if (errno == ELOOP) {
+ struct fsck_ref_report report = { 0 };
+ report.path = "packed-refs";
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_REF_FILETYPE,
+ "not a regular file but a symlink");
+ goto cleanup;
+ }
+
+ ret = error_errno(_("unable to open '%s'"), refs->path);
+ goto cleanup;
+ } else if (fstat(fd, &st) < 0) {
+ ret = error_errno(_("unable to stat '%s'"), refs->path);
+ goto cleanup;
+ } else if (!S_ISREG(st.st_mode)) {
+ struct fsck_ref_report report = { 0 };
+ report.path = "packed-refs";
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_REF_FILETYPE,
+ "not a regular file");
+ goto cleanup;
+ }
+
+ if (strbuf_read(&packed_ref_content, fd, 0) < 0) {
+ ret = error_errno(_("unable to read '%s'"), refs->path);
+ goto cleanup;
+ }
+
+ ret = packed_fsck_ref_content(o, ref_store, &sorted, packed_ref_content.buf,
+ packed_ref_content.buf + packed_ref_content.len);
+ if (!ret && sorted)
+ ret = packed_fsck_ref_sorted(o, ref_store, packed_ref_content.buf,
+ packed_ref_content.buf + packed_ref_content.len);
+
+cleanup:
+ if (fd >= 0)
+ close(fd);
+ strbuf_release(&packed_ref_content);
+ return ret;
}
struct ref_storage_be refs_be_packed = {
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 02f09e4df8..c1f1bab1d5 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -362,9 +362,7 @@ struct cache_ref_iterator {
struct ref_iterator base;
/*
- * The number of levels currently on the stack. This is always
- * at least 1, because when it becomes zero the iteration is
- * ended and this struct is freed.
+ * The number of levels currently on the stack.
*/
size_t levels_nr;
@@ -376,7 +374,7 @@ struct cache_ref_iterator {
* The prefix is matched textually, without regard for path
* component boundaries.
*/
- const char *prefix;
+ char *prefix;
/*
* A stack of levels. levels[0] is the uppermost level that is
@@ -389,6 +387,9 @@ struct cache_ref_iterator {
struct cache_ref_iterator_level *levels;
struct repository *repo;
+ struct ref_cache *cache;
+
+ int prime_dir;
};
static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
@@ -396,6 +397,9 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
+ if (!iter->levels_nr)
+ return ITER_DONE;
+
while (1) {
struct cache_ref_iterator_level *level =
&iter->levels[iter->levels_nr - 1];
@@ -409,7 +413,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
if (++level->index == level->dir->nr) {
/* This level is exhausted; pop up a level */
if (--iter->levels_nr == 0)
- return ref_iterator_abort(ref_iterator);
+ return ITER_DONE;
continue;
}
@@ -444,6 +448,41 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
}
}
+static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct cache_ref_iterator *iter =
+ (struct cache_ref_iterator *)ref_iterator;
+ struct cache_ref_iterator_level *level;
+ struct ref_dir *dir;
+
+ dir = get_ref_dir(iter->cache->root);
+ if (prefix && *prefix)
+ dir = find_containing_dir(dir, prefix);
+ if (!dir) {
+ iter->levels_nr = 0;
+ return 0;
+ }
+
+ if (iter->prime_dir)
+ prime_ref_dir(dir, prefix);
+ iter->levels_nr = 1;
+ level = &iter->levels[0];
+ level->index = -1;
+ level->dir = dir;
+
+ if (prefix && *prefix) {
+ free(iter->prefix);
+ iter->prefix = xstrdup(prefix);
+ level->prefix_state = PREFIX_WITHIN_DIR;
+ } else {
+ FREE_AND_NULL(iter->prefix);
+ level->prefix_state = PREFIX_CONTAINS_DIR;
+ }
+
+ return 0;
+}
+
static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -452,21 +491,19 @@ static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
return peel_object(iter->repo, ref_iterator->oid, peeled) ? -1 : 0;
}
-static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void cache_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
-
- free((char *)iter->prefix);
+ free(iter->prefix);
free(iter->levels);
- base_ref_iterator_free(ref_iterator);
- return ITER_DONE;
}
static struct ref_iterator_vtable cache_ref_iterator_vtable = {
.advance = cache_ref_iterator_advance,
+ .seek = cache_ref_iterator_seek,
.peel = cache_ref_iterator_peel,
- .abort = cache_ref_iterator_abort
+ .release = cache_ref_iterator_release,
};
struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
@@ -474,39 +511,22 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
struct repository *repo,
int prime_dir)
{
- struct ref_dir *dir;
struct cache_ref_iterator *iter;
struct ref_iterator *ref_iterator;
- struct cache_ref_iterator_level *level;
-
- dir = get_ref_dir(cache->root);
- if (prefix && *prefix)
- dir = find_containing_dir(dir, prefix);
- if (!dir)
- /* There's nothing to iterate over. */
- return empty_ref_iterator_begin();
-
- if (prime_dir)
- prime_ref_dir(dir, prefix);
CALLOC_ARRAY(iter, 1);
ref_iterator = &iter->base;
base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable);
ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
- iter->levels_nr = 1;
- level = &iter->levels[0];
- level->index = -1;
- level->dir = dir;
+ iter->repo = repo;
+ iter->cache = cache;
+ iter->prime_dir = prime_dir;
- if (prefix && *prefix) {
- iter->prefix = xstrdup(prefix);
- level->prefix_state = PREFIX_WITHIN_DIR;
- } else {
- level->prefix_state = PREFIX_CONTAINS_DIR;
+ if (cache_ref_iterator_seek(&iter->base, prefix) < 0) {
+ ref_iterator_free(&iter->base);
+ return NULL;
}
- iter->repo = repo;
-
return ref_iterator;
}
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 8894b43d1d..e5862757a7 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -273,11 +273,11 @@ enum do_for_each_ref_flags {
* the next reference and returns ITER_OK. The data pointed at by
* refname and oid belong to the iterator; if you want to retain them
* after calling ref_iterator_advance() again or calling
- * ref_iterator_abort(), you must make a copy. When the iteration has
+ * ref_iterator_free(), you must make a copy. When the iteration has
* been exhausted, ref_iterator_advance() releases any resources
* associated with the iteration, frees the ref_iterator object, and
* returns ITER_DONE. If you want to abort the iteration early, call
- * ref_iterator_abort(), which also frees the ref_iterator object and
+ * ref_iterator_free(), which also frees the ref_iterator object and
* any associated resources. If there was an internal error advancing
* to the next entry, ref_iterator_advance() aborts the iteration,
* frees the ref_iterator, and returns ITER_ERROR.
@@ -293,7 +293,7 @@ enum do_for_each_ref_flags {
*
* while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
* if (want_to_stop_iteration()) {
- * ok = ref_iterator_abort(iter);
+ * ok = ITER_DONE;
* break;
* }
*
@@ -307,6 +307,7 @@ enum do_for_each_ref_flags {
*
* if (ok != ITER_DONE)
* handle_error();
+ * ref_iterator_free(iter);
*/
struct ref_iterator {
struct ref_iterator_vtable *vtable;
@@ -327,18 +328,30 @@ struct ref_iterator {
int ref_iterator_advance(struct ref_iterator *ref_iterator);
/*
+ * Seek the iterator to the first reference with the given prefix.
+ * The prefix is matched as a literal string, without regard for path
+ * separators. If prefix is NULL or the empty string, seek the iterator to the
+ * first reference again.
+ *
+ * This function is expected to behave as if a new ref iterator with the same
+ * prefix had been created, but allows reuse of iterators and thus may allow
+ * the backend to optimize. Parameters other than the prefix that have been
+ * passed when creating the iterator will remain unchanged.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix);
+
+/*
* If possible, peel the reference currently being viewed by the
* iterator. Return 0 on success.
*/
int ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled);
-/*
- * End the iteration before it has been exhausted, freeing the
- * reference iterator and any associated resources and returning
- * ITER_DONE. If the abort itself failed, return ITER_ERROR.
- */
-int ref_iterator_abort(struct ref_iterator *ref_iterator);
+/* Free the reference iterator and any associated resources. */
+void ref_iterator_free(struct ref_iterator *ref_iterator);
/*
* An iterator over nothing (its first ref_iterator_advance() call
@@ -438,13 +451,6 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
void base_ref_iterator_init(struct ref_iterator *iter,
struct ref_iterator_vtable *vtable);
-/*
- * Base class destructor for ref_iterators. Destroy the ref_iterator
- * part of iter and shallow-free the object. This is meant to be
- * called only by the destructors of derived classes.
- */
-void base_ref_iterator_free(struct ref_iterator *iter);
-
/* Virtual function declarations for ref_iterators: */
/*
@@ -456,6 +462,13 @@ void base_ref_iterator_free(struct ref_iterator *iter);
typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator);
/*
+ * Seek the iterator to the first reference matching the given prefix. Should
+ * behave the same as if a new iterator was created with the same prefix.
+ */
+typedef int ref_iterator_seek_fn(struct ref_iterator *ref_iterator,
+ const char *prefix);
+
+/*
* Peels the current ref, returning 0 for success or -1 for failure.
*/
typedef int ref_iterator_peel_fn(struct ref_iterator *ref_iterator,
@@ -463,15 +476,15 @@ typedef int ref_iterator_peel_fn(struct ref_iterator *ref_iterator,
/*
* Implementations of this function should free any resources specific
- * to the derived class, then call base_ref_iterator_free() to clean
- * up and free the ref_iterator object.
+ * to the derived class.
*/
-typedef int ref_iterator_abort_fn(struct ref_iterator *ref_iterator);
+typedef void ref_iterator_release_fn(struct ref_iterator *ref_iterator);
struct ref_iterator_vtable {
ref_iterator_advance_fn *advance;
+ ref_iterator_seek_fn *seek;
ref_iterator_peel_fn *peel;
- ref_iterator_abort_fn *abort;
+ ref_iterator_release_fn *release;
};
/*
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 7e90e13f74..ae434cd248 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -380,7 +380,7 @@ static struct ref_store *reftable_be_init(struct repository *repo,
default:
BUG("unknown hash algorithm %d", repo->hash_algo->format_id);
}
- refs->write_options.default_permissions = calc_shared_perm(0666 & ~mask);
+ refs->write_options.default_permissions = calc_shared_perm(the_repository, 0666 & ~mask);
refs->write_options.disable_auto_compact =
!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
refs->write_options.lock_timeout_ms = 100;
@@ -470,21 +470,21 @@ static int reftable_be_create_on_disk(struct ref_store *ref_store,
struct strbuf sb = STRBUF_INIT;
strbuf_addf(&sb, "%s/reftable", refs->base.gitdir);
- safe_create_dir(sb.buf, 1);
+ safe_create_dir(the_repository, 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);
+ adjust_shared_perm(the_repository, sb.buf);
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/refs", refs->base.gitdir);
- safe_create_dir(sb.buf, 1);
+ safe_create_dir(the_repository, 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);
+ adjust_shared_perm(the_repository, sb.buf);
strbuf_release(&sb);
return 0;
@@ -547,7 +547,7 @@ struct reftable_ref_iterator {
struct reftable_ref_record ref;
struct object_id oid;
- const char *prefix;
+ char *prefix;
size_t prefix_len;
char **exclude_patterns;
size_t exclude_patterns_index;
@@ -711,20 +711,27 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
break;
}
- if (iter->err > 0) {
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- return ITER_ERROR;
+ if (iter->err > 0)
return ITER_DONE;
- }
-
- if (iter->err < 0) {
- ref_iterator_abort(ref_iterator);
+ if (iter->err < 0)
return ITER_ERROR;
- }
-
return ITER_OK;
}
+static int reftable_ref_iterator_seek(struct ref_iterator *ref_iterator,
+ const char *prefix)
+{
+ struct reftable_ref_iterator *iter =
+ (struct reftable_ref_iterator *)ref_iterator;
+
+ free(iter->prefix);
+ iter->prefix = xstrdup_or_null(prefix);
+ iter->prefix_len = prefix ? strlen(prefix) : 0;
+ iter->err = reftable_iterator_seek_ref(&iter->iter, prefix);
+
+ return iter->err;
+}
+
static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
@@ -740,7 +747,7 @@ static int reftable_ref_iterator_peel(struct ref_iterator *ref_iterator,
return -1;
}
-static int reftable_ref_iterator_abort(struct ref_iterator *ref_iterator)
+static void reftable_ref_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_ref_iterator *iter =
(struct reftable_ref_iterator *)ref_iterator;
@@ -751,14 +758,14 @@ static int reftable_ref_iterator_abort(struct ref_iterator *ref_iterator)
free(iter->exclude_patterns[i]);
free(iter->exclude_patterns);
}
- free(iter);
- return ITER_DONE;
+ free(iter->prefix);
}
static struct ref_iterator_vtable reftable_ref_iterator_vtable = {
.advance = reftable_ref_iterator_advance,
+ .seek = reftable_ref_iterator_seek,
.peel = reftable_ref_iterator_peel,
- .abort = reftable_ref_iterator_abort
+ .release = reftable_ref_iterator_release,
};
static int qsort_strcmp(const void *va, const void *vb)
@@ -815,8 +822,6 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
iter = xcalloc(1, sizeof(*iter));
base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable);
- iter->prefix = prefix;
- iter->prefix_len = prefix ? strlen(prefix) : 0;
iter->base.oid = &iter->oid;
iter->flags = flags;
iter->refs = refs;
@@ -830,8 +835,11 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_
if (ret)
goto done;
- reftable_stack_init_ref_iterator(stack, &iter->iter);
- ret = reftable_iterator_seek_ref(&iter->iter, prefix);
+ ret = reftable_stack_init_ref_iterator(stack, &iter->iter);
+ if (ret)
+ goto done;
+
+ ret = reftable_ref_iterator_seek(&iter->base, prefix);
if (ret)
goto done;
@@ -1069,6 +1077,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
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 string_list refnames_to_check = STRING_LIST_INIT_NODUP;
struct reftable_transaction_data *tx_data = NULL;
struct reftable_backend *be;
struct object_id head_oid;
@@ -1224,12 +1233,7 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
* 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,
- transaction->flags & REF_TRANSACTION_FLAG_INITIAL,
- err);
- if (ret < 0)
- goto done;
+ string_list_append(&refnames_to_check, u->refname);
/*
* There is no need to write the reference deletion
@@ -1379,6 +1383,12 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store,
}
}
+ ret = refs_verify_refnames_available(ref_store, &refnames_to_check, &affected_refnames, NULL,
+ transaction->flags & REF_TRANSACTION_FLAG_INITIAL,
+ err);
+ if (ret < 0)
+ goto done;
+
transaction->backend_data = tx_data;
transaction->state = REF_TRANSACTION_PREPARED;
@@ -1394,6 +1404,7 @@ done:
string_list_clear(&affected_refnames, 0);
strbuf_release(&referent);
strbuf_release(&head_referent);
+ string_list_clear(&refnames_to_check, 0);
return ret;
}
@@ -2017,20 +2028,20 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator)
break;
}
- if (iter->err > 0) {
- if (ref_iterator_abort(ref_iterator) != ITER_DONE)
- return ITER_ERROR;
+ if (iter->err > 0)
return ITER_DONE;
- }
-
- if (iter->err < 0) {
- ref_iterator_abort(ref_iterator);
+ if (iter->err < 0)
return ITER_ERROR;
- }
-
return ITER_OK;
}
+static int reftable_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED,
+ const char *prefix UNUSED)
+{
+ BUG("reftable reflog iterator cannot be seeked");
+ return -1;
+}
+
static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSED,
struct object_id *peeled UNUSED)
{
@@ -2038,21 +2049,20 @@ static int reftable_reflog_iterator_peel(struct ref_iterator *ref_iterator UNUSE
return -1;
}
-static int reftable_reflog_iterator_abort(struct ref_iterator *ref_iterator)
+static void reftable_reflog_iterator_release(struct ref_iterator *ref_iterator)
{
struct reftable_reflog_iterator *iter =
(struct reftable_reflog_iterator *)ref_iterator;
reftable_log_record_release(&iter->log);
reftable_iterator_destroy(&iter->iter);
strbuf_release(&iter->last_name);
- free(iter);
- return ITER_DONE;
}
static struct ref_iterator_vtable reftable_reflog_iterator_vtable = {
.advance = reftable_reflog_iterator_advance,
+ .seek = reftable_reflog_iterator_seek,
.peel = reftable_reflog_iterator_peel,
- .abort = reftable_reflog_iterator_abort
+ .release = reftable_reflog_iterator_release,
};
static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftable_ref_store *refs,
diff --git a/refspec.c b/refspec.c
index 4cb80b5208..0775358d96 100644
--- a/refspec.c
+++ b/refspec.c
@@ -153,18 +153,22 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet
return 1;
}
-int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)
+static int refspec_item_init(struct refspec_item *item, const char *refspec,
+ int fetch)
{
memset(item, 0, sizeof(*item));
item->raw = xstrdup(refspec);
return parse_refspec(item, refspec, fetch);
}
-void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,
- int fetch)
+int refspec_item_init_fetch(struct refspec_item *item, const char *refspec)
{
- if (!refspec_item_init(item, refspec, fetch))
- die(_("invalid refspec '%s'"), refspec);
+ return refspec_item_init(item, refspec, 1);
+}
+
+int refspec_item_init_push(struct refspec_item *item, const char *refspec)
+{
+ return refspec_item_init(item, refspec, 0);
}
void refspec_item_clear(struct refspec_item *item)
@@ -178,17 +182,29 @@ void refspec_item_clear(struct refspec_item *item)
item->exact_sha1 = 0;
}
-void refspec_init(struct refspec *rs, int fetch)
+void refspec_init_fetch(struct refspec *rs)
+{
+ struct refspec blank = REFSPEC_INIT_FETCH;
+ memcpy(rs, &blank, sizeof(*rs));
+}
+
+void refspec_init_push(struct refspec *rs)
{
- memset(rs, 0, sizeof(*rs));
- rs->fetch = fetch;
+ struct refspec blank = REFSPEC_INIT_PUSH;
+ memcpy(rs, &blank, sizeof(*rs));
}
void refspec_append(struct refspec *rs, const char *refspec)
{
struct refspec_item item;
+ int ret;
- refspec_item_init_or_die(&item, refspec, rs->fetch);
+ if (rs->fetch)
+ ret = refspec_item_init_fetch(&item, refspec);
+ else
+ ret = refspec_item_init_push(&item, refspec);
+ if (!ret)
+ die(_("invalid refspec '%s'"), refspec);
ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);
rs->items[rs->nr] = item;
@@ -233,7 +249,7 @@ void refspec_clear(struct refspec *rs)
int valid_fetch_refspec(const char *fetch_refspec_str)
{
struct refspec_item refspec;
- int ret = refspec_item_init(&refspec, fetch_refspec_str, REFSPEC_FETCH);
+ int ret = refspec_item_init_fetch(&refspec, fetch_refspec_str);
refspec_item_clear(&refspec);
return ret;
}
@@ -246,14 +262,24 @@ void refspec_ref_prefixes(const struct refspec *rs,
const struct refspec_item *item = &rs->items[i];
const char *prefix = NULL;
- if (item->exact_sha1 || item->negative)
+ if (item->negative)
continue;
- if (rs->fetch == REFSPEC_FETCH)
- prefix = item->src;
- else if (item->dst)
- prefix = item->dst;
- else if (item->src && !item->exact_sha1)
+
+ if (rs->fetch) {
+ if (item->exact_sha1)
+ continue;
prefix = item->src;
+ } else {
+ /*
+ * Pushes can have an explicit destination like
+ * "foo:bar", or can implicitly use the src for both
+ * ("foo" is the same as "foo:foo").
+ */
+ if (item->dst)
+ prefix = item->dst;
+ else if (item->src && !item->exact_sha1)
+ prefix = item->src;
+ }
if (!prefix)
continue;
diff --git a/refspec.h b/refspec.h
index e2b5cc54ef..8b04f9995e 100644
--- a/refspec.h
+++ b/refspec.h
@@ -32,11 +32,8 @@ struct refspec_item {
struct string_list;
-#define REFSPEC_FETCH 1
-#define REFSPEC_PUSH 0
-
-#define REFSPEC_INIT_FETCH { .fetch = REFSPEC_FETCH }
-#define REFSPEC_INIT_PUSH { .fetch = REFSPEC_PUSH }
+#define REFSPEC_INIT_FETCH { .fetch = 1 }
+#define REFSPEC_INIT_PUSH { .fetch = 0 }
/**
* An array of strings can be parsed into a struct refspec using
@@ -47,15 +44,14 @@ struct refspec {
int alloc;
int nr;
- int fetch;
+ unsigned fetch : 1;
};
-int refspec_item_init(struct refspec_item *item, const char *refspec,
- int fetch);
-void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,
- int fetch);
+int refspec_item_init_fetch(struct refspec_item *item, const char *refspec);
+int refspec_item_init_push(struct refspec_item *item, const char *refspec);
void refspec_item_clear(struct refspec_item *item);
-void refspec_init(struct refspec *rs, int fetch);
+void refspec_init_fetch(struct refspec *rs);
+void refspec_init_push(struct refspec *rs);
void refspec_append(struct refspec *rs, const char *refspec);
__attribute__((format (printf,2,3)))
void refspec_appendf(struct refspec *rs, const char *fmt, ...);
diff --git a/reftable/basics.c b/reftable/basics.c
index 3b5ea27bbd..8c4a4433e4 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -147,25 +147,6 @@ char *reftable_buf_detach(struct reftable_buf *buf)
return result;
}
-void put_be24(uint8_t *out, uint32_t i)
-{
- out[0] = (uint8_t)((i >> 16) & 0xff);
- out[1] = (uint8_t)((i >> 8) & 0xff);
- out[2] = (uint8_t)(i & 0xff);
-}
-
-uint32_t get_be24(uint8_t *in)
-{
- return (uint32_t)(in[0]) << 16 | (uint32_t)(in[1]) << 8 |
- (uint32_t)(in[2]);
-}
-
-void put_be16(uint8_t *out, uint16_t i)
-{
- out[0] = (uint8_t)((i >> 8) & 0xff);
- out[1] = (uint8_t)(i & 0xff);
-}
-
size_t binsearch(size_t sz, int (*f)(size_t k, void *args), void *args)
{
size_t lo = 0;
diff --git a/reftable/basics.h b/reftable/basics.h
index a2a010a0e1..fd59cbb772 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -16,6 +16,8 @@ https://developers.google.com/open-source/licenses/bsd
#include "system.h"
#include "reftable-basics.h"
+#define REFTABLE_UNUSED __attribute__((__unused__))
+
struct reftable_buf {
size_t alloc;
size_t len;
@@ -76,9 +78,79 @@ char *reftable_buf_detach(struct reftable_buf *buf);
/* Bigendian en/decoding of integers */
-void put_be24(uint8_t *out, uint32_t i);
-uint32_t get_be24(uint8_t *in);
-void put_be16(uint8_t *out, uint16_t i);
+static inline void reftable_put_be16(void *out, uint16_t i)
+{
+ unsigned char *p = out;
+ p[0] = (uint8_t)((i >> 8) & 0xff);
+ p[1] = (uint8_t)((i >> 0) & 0xff);
+}
+
+static inline void reftable_put_be24(void *out, uint32_t i)
+{
+ unsigned char *p = out;
+ p[0] = (uint8_t)((i >> 16) & 0xff);
+ p[1] = (uint8_t)((i >> 8) & 0xff);
+ p[2] = (uint8_t)((i >> 0) & 0xff);
+}
+
+static inline void reftable_put_be32(void *out, uint32_t i)
+{
+ unsigned char *p = out;
+ p[0] = (uint8_t)((i >> 24) & 0xff);
+ p[1] = (uint8_t)((i >> 16) & 0xff);
+ p[2] = (uint8_t)((i >> 8) & 0xff);
+ p[3] = (uint8_t)((i >> 0) & 0xff);
+}
+
+static inline void reftable_put_be64(void *out, uint64_t i)
+{
+ unsigned char *p = out;
+ p[0] = (uint8_t)((i >> 56) & 0xff);
+ p[1] = (uint8_t)((i >> 48) & 0xff);
+ p[2] = (uint8_t)((i >> 40) & 0xff);
+ p[3] = (uint8_t)((i >> 32) & 0xff);
+ p[4] = (uint8_t)((i >> 24) & 0xff);
+ p[5] = (uint8_t)((i >> 16) & 0xff);
+ p[6] = (uint8_t)((i >> 8) & 0xff);
+ p[7] = (uint8_t)((i >> 0) & 0xff);
+}
+
+static inline uint16_t reftable_get_be16(const void *in)
+{
+ const unsigned char *p = in;
+ return (uint16_t)(p[0]) << 8 |
+ (uint16_t)(p[1]) << 0;
+}
+
+static inline uint32_t reftable_get_be24(const void *in)
+{
+ const unsigned char *p = in;
+ return (uint32_t)(p[0]) << 16 |
+ (uint32_t)(p[1]) << 8 |
+ (uint32_t)(p[2]) << 0;
+}
+
+static inline uint32_t reftable_get_be32(const void *in)
+{
+ const unsigned char *p = in;
+ return (uint32_t)(p[0]) << 24 |
+ (uint32_t)(p[1]) << 16 |
+ (uint32_t)(p[2]) << 8|
+ (uint32_t)(p[3]) << 0;
+}
+
+static inline uint64_t reftable_get_be64(const void *in)
+{
+ const unsigned char *p = in;
+ return (uint64_t)(p[0]) << 56 |
+ (uint64_t)(p[1]) << 48 |
+ (uint64_t)(p[2]) << 40 |
+ (uint64_t)(p[3]) << 32 |
+ (uint64_t)(p[4]) << 24 |
+ (uint64_t)(p[5]) << 16 |
+ (uint64_t)(p[6]) << 8 |
+ (uint64_t)(p[7]) << 0;
+}
/*
* find smallest index i in [0, sz) at which `f(i) > 0`, assuming that f is
@@ -117,18 +189,46 @@ void reftable_free(void *p);
void *reftable_calloc(size_t nelem, size_t elsize);
char *reftable_strdup(const char *str);
-#define REFTABLE_ALLOC_ARRAY(x, alloc) (x) = reftable_malloc(st_mult(sizeof(*(x)), (alloc)))
+static inline int reftable_alloc_size(size_t nelem, size_t elsize, size_t *out)
+{
+ if (nelem && elsize > SIZE_MAX / nelem)
+ return -1;
+ *out = nelem * elsize;
+ return 0;
+}
+
+#define REFTABLE_ALLOC_ARRAY(x, alloc) do { \
+ size_t alloc_size; \
+ if (reftable_alloc_size(sizeof(*(x)), (alloc), &alloc_size) < 0) { \
+ errno = ENOMEM; \
+ (x) = NULL; \
+ } else { \
+ (x) = reftable_malloc(alloc_size); \
+ } \
+ } while (0)
#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_REALLOC_ARRAY(x, alloc) do { \
+ size_t alloc_size; \
+ if (reftable_alloc_size(sizeof(*(x)), (alloc), &alloc_size) < 0) { \
+ errno = ENOMEM; \
+ (x) = NULL; \
+ } else { \
+ (x) = reftable_realloc((x), alloc_size); \
+ } \
+ } while (0)
static inline void *reftable_alloc_grow(void *p, size_t nelem, size_t elsize,
size_t *allocp)
{
void *new_p;
- size_t alloc = *allocp * 2 + 1;
+ size_t alloc = *allocp * 2 + 1, alloc_bytes;
if (alloc < nelem)
alloc = nelem;
- new_p = reftable_realloc(p, st_mult(elsize, alloc));
+ if (reftable_alloc_size(elsize, alloc, &alloc_bytes) < 0) {
+ errno = ENOMEM;
+ return p;
+ }
+ new_p = reftable_realloc(p, alloc_bytes);
if (!new_p)
return p;
*allocp = alloc;
@@ -168,6 +268,15 @@ static inline void *reftable_alloc_grow(void *p, size_t nelem, size_t elsize,
# define strdup(str) REFTABLE_BANNED(strdup)
#endif
+#define REFTABLE_SWAP(a, b) do { \
+ void *_swap_a_ptr = &(a); \
+ void *_swap_b_ptr = &(b); \
+ unsigned char _swap_buffer[sizeof(a) - 2 * sizeof(a) * (sizeof(a) != sizeof(b))]; \
+ memcpy(_swap_buffer, _swap_a_ptr, sizeof(a)); \
+ memcpy(_swap_a_ptr, _swap_b_ptr, sizeof(a)); \
+ memcpy(_swap_b_ptr, _swap_buffer, sizeof(a)); \
+} while (0)
+
/* Find the longest shared prefix size of `a` and `b` */
size_t common_prefix_size(struct reftable_buf *a, struct reftable_buf *b);
diff --git a/reftable/block.c b/reftable/block.c
index b14a8f1259..251a8e9fd3 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -49,7 +49,7 @@ static int block_writer_register_restart(struct block_writer *w, int n,
if (is_restart)
rlen++;
if (2 + 3 * rlen + n > w->block_size - w->next)
- return -1;
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
if (is_restart) {
REFTABLE_ALLOC_GROW_OR_NULL(w->restarts, w->restart_len + 1,
w->restart_cap);
@@ -97,9 +97,10 @@ uint8_t block_writer_type(struct block_writer *bw)
return bw->block[bw->header_off];
}
-/* Adds the reftable_record to the block. Returns -1 if it does not fit, 0 on
- success. Returns REFTABLE_API_ERROR if attempting to write a record with
- empty key. */
+/*
+ * Adds the reftable_record to the block. Returns 0 on success and
+ * appropriate error codes on failure.
+ */
int block_writer_add(struct block_writer *w, struct reftable_record *rec)
{
struct reftable_buf empty = REFTABLE_BUF_INIT;
@@ -126,14 +127,14 @@ int block_writer_add(struct block_writer *w, struct reftable_record *rec)
n = reftable_encode_key(&is_restart, out, last, w->scratch,
reftable_record_val_type(rec));
if (n < 0) {
- err = -1;
+ err = n;
goto done;
}
string_view_consume(&out, n);
n = reftable_record_encode(rec, out, w->hash_size);
if (n < 0) {
- err = -1;
+ err = n;
goto done;
}
string_view_consume(&out, n);
@@ -147,13 +148,13 @@ done:
int block_writer_finish(struct block_writer *w)
{
for (uint32_t i = 0; i < w->restart_len; i++) {
- put_be24(w->block + w->next, w->restarts[i]);
+ reftable_put_be24(w->block + w->next, w->restarts[i]);
w->next += 3;
}
- put_be16(w->block + w->next, w->restart_len);
+ reftable_put_be16(w->block + w->next, w->restart_len);
w->next += 2;
- put_be24(w->block + 1 + w->header_off, w->next);
+ reftable_put_be24(w->block + 1 + w->header_off, w->next);
/*
* Log records are stored zlib-compressed. Note that the compression
@@ -215,7 +216,7 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,
{
uint32_t full_block_size = table_block_size;
uint8_t typ = block->data[header_off];
- uint32_t sz = get_be24(block->data + header_off + 1);
+ uint32_t sz = reftable_get_be24(block->data + header_off + 1);
int err = 0;
uint16_t restart_count = 0;
uint32_t restart_start = 0;
@@ -299,7 +300,7 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,
full_block_size = sz;
}
- restart_count = get_be16(block->data + sz - 2);
+ restart_count = reftable_get_be16(block->data + sz - 2);
restart_start = sz - 2 - 3 * restart_count;
restart_bytes = block->data + restart_start;
@@ -354,7 +355,7 @@ int block_reader_first_key(const struct block_reader *br, struct reftable_buf *k
static uint32_t block_reader_restart_offset(const struct block_reader *br, size_t idx)
{
- return get_be24(br->restart_bytes + 3 * idx);
+ return reftable_get_be24(br->restart_bytes + 3 * idx);
}
void block_iter_seek_start(struct block_iter *it, const struct block_reader *br)
@@ -508,7 +509,9 @@ int block_iter_seek_key(struct block_iter *it, const struct block_reader *br,
it->block_len = br->block_len;
it->hash_size = br->hash_size;
- reftable_record_init(&rec, block_reader_type(br));
+ err = reftable_record_init(&rec, block_reader_type(br));
+ if (err < 0)
+ goto done;
/*
* We're looking for the last entry less than the wanted key so that
diff --git a/reftable/block.h b/reftable/block.h
index bef2b8a4c5..64732eba7d 100644
--- a/reftable/block.h
+++ b/reftable/block.h
@@ -53,7 +53,7 @@ int block_writer_init(struct block_writer *bw, uint8_t typ, uint8_t *block,
/* returns the block type (eg. 'r' for ref records. */
uint8_t block_writer_type(struct block_writer *bw);
-/* appends the record, or -1 if it doesn't fit. */
+/* Attempts to append the record. Returns 0 on success or error code on failure. */
int block_writer_add(struct block_writer *w, struct reftable_record *rec);
/* appends the key restarts, and compress the block if necessary. */
diff --git a/reftable/blocksource.c b/reftable/blocksource.c
index bba4a45b98..78c1be2337 100644
--- a/reftable/blocksource.c
+++ b/reftable/blocksource.c
@@ -13,14 +13,14 @@ https://developers.google.com/open-source/licenses/bsd
#include "reftable-blocksource.h"
#include "reftable-error.h"
-static void reftable_buf_return_block(void *b UNUSED, struct reftable_block *dest)
+static void reftable_buf_return_block(void *b REFTABLE_UNUSED, struct reftable_block *dest)
{
if (dest->len)
memset(dest->data, 0xff, dest->len);
reftable_free(dest->data);
}
-static void reftable_buf_close(void *b UNUSED)
+static void reftable_buf_close(void *b REFTABLE_UNUSED)
{
}
@@ -67,7 +67,7 @@ static uint64_t file_size(void *b)
return ((struct file_block_source *)b)->size;
}
-static void file_return_block(void *b UNUSED, struct reftable_block *dest UNUSED)
+static void file_return_block(void *b REFTABLE_UNUSED, struct reftable_block *dest REFTABLE_UNUSED)
{
}
@@ -98,7 +98,7 @@ static struct reftable_block_source_vtable file_vtable = {
int reftable_block_source_from_file(struct reftable_block_source *bs,
const char *name)
{
- struct file_block_source *p;
+ struct file_block_source *p = NULL;
struct stat st;
int fd, err;
@@ -122,7 +122,12 @@ int reftable_block_source_from_file(struct reftable_block_source *bs,
}
p->size = st.st_size;
- p->data = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ p->data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (p->data == MAP_FAILED) {
+ err = REFTABLE_IO_ERROR;
+ p->data = NULL;
+ goto out;
+ }
assert(!bs->ops);
bs->ops = &file_vtable;
@@ -135,5 +140,5 @@ out:
close(fd);
if (err < 0)
reftable_free(p);
- return 0;
+ return err;
}
diff --git a/reftable/iter.c b/reftable/iter.c
index 86e801ca9f..f520382e70 100644
--- a/reftable/iter.c
+++ b/reftable/iter.c
@@ -25,17 +25,17 @@ int iterator_next(struct reftable_iterator *it, struct reftable_record *rec)
return it->ops->next(it->iter_arg, rec);
}
-static int empty_iterator_seek(void *arg UNUSED, struct reftable_record *want UNUSED)
+static int empty_iterator_seek(void *arg REFTABLE_UNUSED, struct reftable_record *want REFTABLE_UNUSED)
{
return 0;
}
-static int empty_iterator_next(void *arg UNUSED, struct reftable_record *rec UNUSED)
+static int empty_iterator_next(void *arg REFTABLE_UNUSED, struct reftable_record *rec REFTABLE_UNUSED)
{
return 1;
}
-static void empty_iterator_close(void *arg UNUSED)
+static void empty_iterator_close(void *arg REFTABLE_UNUSED)
{
}
@@ -143,11 +143,10 @@ static int indexed_table_ref_iter_next_block(struct indexed_table_ref_iter *it)
return 0;
}
-static int indexed_table_ref_iter_seek(void *p UNUSED,
- struct reftable_record *want UNUSED)
+static int indexed_table_ref_iter_seek(void *p REFTABLE_UNUSED,
+ struct reftable_record *want REFTABLE_UNUSED)
{
- BUG("seeking indexed table is not supported");
- return -1;
+ return REFTABLE_API_ERROR;
}
static int indexed_table_ref_iter_next(void *p, struct reftable_record *rec)
diff --git a/reftable/merged.c b/reftable/merged.c
index e72b39e178..4ff1553772 100644
--- a/reftable/merged.c
+++ b/reftable/merged.c
@@ -66,8 +66,11 @@ static int merged_iter_seek(struct merged_iter *mi, struct reftable_record *want
int err;
mi->advance_index = -1;
- while (!merged_iter_pqueue_is_empty(mi->pq))
- merged_iter_pqueue_remove(&mi->pq);
+ while (!merged_iter_pqueue_is_empty(mi->pq)) {
+ err = merged_iter_pqueue_remove(&mi->pq, NULL);
+ if (err < 0)
+ return err;
+ }
for (size_t i = 0; i < mi->subiters_len; i++) {
err = iterator_seek(&mi->subiters[i].iter, want);
@@ -120,7 +123,9 @@ static int merged_iter_next_entry(struct merged_iter *mi,
if (empty)
return 1;
- entry = merged_iter_pqueue_remove(&mi->pq);
+ err = merged_iter_pqueue_remove(&mi->pq, &entry);
+ if (err < 0)
+ return err;
/*
One can also use reftable as datacenter-local storage, where the ref
@@ -134,18 +139,23 @@ static int merged_iter_next_entry(struct merged_iter *mi,
struct pq_entry top = merged_iter_pqueue_top(mi->pq);
int cmp;
- cmp = reftable_record_cmp(top.rec, entry.rec);
+ err = reftable_record_cmp(top.rec, entry.rec, &cmp);
+ if (err < 0)
+ return err;
if (cmp > 0)
break;
- merged_iter_pqueue_remove(&mi->pq);
+ err = merged_iter_pqueue_remove(&mi->pq, NULL);
+ if (err < 0)
+ return err;
+
err = merged_iter_advance_subiter(mi, top.index);
if (err < 0)
return err;
}
mi->advance_index = entry.index;
- SWAP(*rec, *entry.rec);
+ REFTABLE_SWAP(*rec, *entry.rec);
return 0;
}
@@ -253,7 +263,10 @@ int merged_table_init_iter(struct reftable_merged_table *mt,
}
for (size_t i = 0; i < mt->readers_len; i++) {
- reftable_record_init(&subiters[i].rec, typ);
+ ret = reftable_record_init(&subiters[i].rec, typ);
+ if (ret < 0)
+ goto out;
+
ret = reader_init_iter(mt->readers[i], &subiters[i].iter, typ);
if (ret < 0)
goto out;
diff --git a/reftable/pq.c b/reftable/pq.c
index 5591e875e1..82394a972d 100644
--- a/reftable/pq.c
+++ b/reftable/pq.c
@@ -15,13 +15,18 @@ https://developers.google.com/open-source/licenses/bsd
int pq_less(struct pq_entry *a, struct pq_entry *b)
{
- int cmp = reftable_record_cmp(a->rec, b->rec);
+ int cmp, err;
+
+ err = reftable_record_cmp(a->rec, b->rec, &cmp);
+ if (err < 0)
+ return err;
+
if (cmp == 0)
return a->index > b->index;
return cmp < 0;
}
-struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq)
+int merged_iter_pqueue_remove(struct merged_iter_pqueue *pq, struct pq_entry *out)
{
size_t i = 0;
struct pq_entry e = pq->heap[0];
@@ -32,17 +37,34 @@ struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq)
size_t min = i;
size_t j = 2 * i + 1;
size_t k = 2 * i + 2;
- if (j < pq->len && pq_less(&pq->heap[j], &pq->heap[i]))
- min = j;
- if (k < pq->len && pq_less(&pq->heap[k], &pq->heap[min]))
- min = k;
+ int cmp;
+
+ if (j < pq->len) {
+ cmp = pq_less(&pq->heap[j], &pq->heap[i]);
+ if (cmp < 0)
+ return -1;
+ else if (cmp)
+ min = j;
+ }
+
+ if (k < pq->len) {
+ cmp = pq_less(&pq->heap[k], &pq->heap[min]);
+ if (cmp < 0)
+ return -1;
+ else if (cmp)
+ min = k;
+ }
+
if (min == i)
break;
- SWAP(pq->heap[i], pq->heap[min]);
+ REFTABLE_SWAP(pq->heap[i], pq->heap[min]);
i = min;
}
- return e;
+ if (out)
+ *out = e;
+
+ return 0;
}
int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e)
@@ -59,7 +81,7 @@ int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry
size_t j = (i - 1) / 2;
if (pq_less(&pq->heap[j], &pq->heap[i]))
break;
- SWAP(pq->heap[j], pq->heap[i]);
+ REFTABLE_SWAP(pq->heap[j], pq->heap[i]);
i = j;
}
diff --git a/reftable/pq.h b/reftable/pq.h
index 83c062eeca..ff39016445 100644
--- a/reftable/pq.h
+++ b/reftable/pq.h
@@ -22,7 +22,7 @@ struct merged_iter_pqueue {
size_t cap;
};
-struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq);
+int merged_iter_pqueue_remove(struct merged_iter_pqueue *pq, struct pq_entry *out);
int merged_iter_pqueue_add(struct merged_iter_pqueue *pq, const struct pq_entry *e);
void merged_iter_pqueue_release(struct merged_iter_pqueue *pq);
int pq_less(struct pq_entry *a, struct pq_entry *b);
diff --git a/reftable/reader.c b/reftable/reader.c
index 3f2e4b2800..172aff2c10 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -101,18 +101,18 @@ static int parse_footer(struct reftable_reader *r, uint8_t *footer,
}
f++;
- r->block_size = get_be24(f);
+ r->block_size = reftable_get_be24(f);
f += 3;
- r->min_update_index = get_be64(f);
+ r->min_update_index = reftable_get_be64(f);
f += 8;
- r->max_update_index = get_be64(f);
+ r->max_update_index = reftable_get_be64(f);
f += 8;
if (r->version == 1) {
r->hash_id = REFTABLE_HASH_SHA1;
} else {
- switch (get_be32(f)) {
+ switch (reftable_get_be32(f)) {
case REFTABLE_FORMAT_ID_SHA1:
r->hash_id = REFTABLE_HASH_SHA1;
break;
@@ -127,24 +127,24 @@ static int parse_footer(struct reftable_reader *r, uint8_t *footer,
f += 4;
}
- r->ref_offsets.index_offset = get_be64(f);
+ r->ref_offsets.index_offset = reftable_get_be64(f);
f += 8;
- r->obj_offsets.offset = get_be64(f);
+ r->obj_offsets.offset = reftable_get_be64(f);
f += 8;
r->object_id_len = r->obj_offsets.offset & ((1 << 5) - 1);
r->obj_offsets.offset >>= 5;
- r->obj_offsets.index_offset = get_be64(f);
+ r->obj_offsets.index_offset = reftable_get_be64(f);
f += 8;
- r->log_offsets.offset = get_be64(f);
+ r->log_offsets.offset = reftable_get_be64(f);
f += 8;
- r->log_offsets.index_offset = get_be64(f);
+ r->log_offsets.index_offset = reftable_get_be64(f);
f += 8;
computed_crc = crc32(0, footer, f - footer);
- file_crc = get_be32(f);
+ file_crc = reftable_get_be32(f);
f += 4;
if (computed_crc != file_crc) {
err = REFTABLE_FORMAT_ERROR;
@@ -214,7 +214,7 @@ static int32_t extract_block_size(uint8_t *data, uint8_t *typ, uint64_t off,
*typ = data[0];
if (reftable_is_block_type(*typ)) {
- result = get_be24(data + 1);
+ result = reftable_get_be24(data + 1);
}
return result;
}
@@ -360,7 +360,10 @@ static int table_iter_seek_linear(struct table_iter *ti,
struct reftable_record rec;
int err;
- reftable_record_init(&rec, reftable_record_type(want));
+ err = reftable_record_init(&rec, reftable_record_type(want));
+ if (err < 0)
+ goto done;
+
err = reftable_record_key(want, &want_key);
if (err < 0)
goto done;
@@ -666,6 +669,8 @@ done:
reftable_block_done(&footer);
reftable_block_done(&header);
if (err) {
+ if (r)
+ reftable_free(r->name);
reftable_free(r);
block_source_close(source);
}
@@ -674,8 +679,6 @@ done:
void reftable_reader_incref(struct reftable_reader *r)
{
- if (!r->refcount)
- BUG("cannot increment ref counter of dead reader");
r->refcount++;
}
@@ -683,8 +686,6 @@ void reftable_reader_decref(struct reftable_reader *r)
{
if (!r)
return;
- if (!r->refcount)
- BUG("cannot decrement ref counter of dead reader");
if (--r->refcount)
return;
block_source_close(&r->source);
@@ -850,7 +851,7 @@ int reftable_reader_print_blocks(const char *tablename)
printf("header:\n");
printf(" block_size: %d\n", r->block_size);
- for (i = 0; i < ARRAY_SIZE(sections); i++) {
+ for (i = 0; i < sizeof(sections) / sizeof(*sections); i++) {
err = table_iter_seek_start(&ti, sections[i].type, 0);
if (err < 0)
goto done;
diff --git a/reftable/record.c b/reftable/record.c
index 8919df8a4d..c0080024ed 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -61,7 +61,7 @@ int put_var_int(struct string_view *dest, uint64_t value)
while (value >>= 7)
varint[--pos] = 0x80 | (--value & 0x7f);
if (dest->len < sizeof(varint) - pos)
- return -1;
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
memcpy(dest->buf, varint + pos, sizeof(varint) - pos);
return sizeof(varint) - pos;
}
@@ -129,10 +129,10 @@ static int encode_string(const char *str, struct string_view s)
size_t l = strlen(str);
int n = put_var_int(&s, l);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
if (s.len < l)
- return -1;
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
memcpy(s.buf, str, l);
string_view_consume(&s, l);
@@ -148,18 +148,18 @@ int reftable_encode_key(int *restart, struct string_view dest,
uint64_t suffix_len = key.len - prefix_len;
int n = put_var_int(&dest, prefix_len);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&dest, n);
*restart = (prefix_len == 0);
n = put_var_int(&dest, suffix_len << 3 | (uint64_t)extra);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&dest, n);
if (dest.len < suffix_len)
- return -1;
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
memcpy(dest.buf, key.buf + prefix_len, suffix_len);
string_view_consume(&dest, suffix_len);
@@ -237,11 +237,11 @@ static int reftable_ref_record_copy_from(void *rec, const void *src_rec,
size_t refname_cap = 0;
int err;
- SWAP(refname, ref->refname);
- SWAP(refname_cap, ref->refname_cap);
+ REFTABLE_SWAP(refname, ref->refname);
+ REFTABLE_SWAP(refname_cap, ref->refname_cap);
reftable_ref_record_release(ref);
- SWAP(ref->refname, refname);
- SWAP(ref->refname_cap, refname_cap);
+ REFTABLE_SWAP(ref->refname, refname);
+ REFTABLE_SWAP(ref->refname_cap, refname_cap);
if (src->refname) {
size_t refname_len = strlen(src->refname);
@@ -324,30 +324,27 @@ static int reftable_ref_record_encode(const void *rec, struct string_view s,
struct string_view start = s;
int n = put_var_int(&s, r->update_index);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
switch (r->value_type) {
case REFTABLE_REF_SYMREF:
n = encode_string(r->value.symref, s);
- if (n < 0) {
- return -1;
- }
+ if (n < 0)
+ return n;
string_view_consume(&s, n);
break;
case REFTABLE_REF_VAL2:
- if (s.len < 2 * hash_size) {
- return -1;
- }
+ if (s.len < 2 * hash_size)
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
memcpy(s.buf, r->value.val2.value, hash_size);
string_view_consume(&s, hash_size);
memcpy(s.buf, r->value.val2.target_value, hash_size);
string_view_consume(&s, hash_size);
break;
case REFTABLE_REF_VAL1:
- if (s.len < hash_size) {
- return -1;
- }
+ if (s.len < hash_size)
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
memcpy(s.buf, r->value.val1, hash_size);
string_view_consume(&s, hash_size);
break;
@@ -376,11 +373,11 @@ static int reftable_ref_record_decode(void *rec, struct reftable_buf key,
return n;
string_view_consume(&in, n);
- SWAP(refname, r->refname);
- SWAP(refname_cap, r->refname_cap);
+ REFTABLE_SWAP(refname, r->refname);
+ REFTABLE_SWAP(refname_cap, r->refname_cap);
reftable_ref_record_release(r);
- SWAP(r->refname, refname);
- SWAP(r->refname_cap, refname_cap);
+ REFTABLE_SWAP(r->refname, refname);
+ REFTABLE_SWAP(r->refname_cap, refname_cap);
REFTABLE_ALLOC_GROW_OR_NULL(r->refname, key.len + 1, r->refname_cap);
if (!r->refname) {
@@ -490,7 +487,7 @@ static void reftable_obj_record_release(void *rec)
}
static int reftable_obj_record_copy_from(void *rec, const void *src_rec,
- uint32_t hash_size UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED)
{
struct reftable_obj_record *obj = rec;
const struct reftable_obj_record *src = src_rec;
@@ -504,11 +501,17 @@ static int reftable_obj_record_copy_from(void *rec, const void *src_rec,
if (src->hash_prefix_len)
memcpy(obj->hash_prefix, src->hash_prefix, obj->hash_prefix_len);
- REFTABLE_ALLOC_ARRAY(obj->offsets, src->offset_len);
- if (!obj->offsets)
- return REFTABLE_OUT_OF_MEMORY_ERROR;
- obj->offset_len = src->offset_len;
- COPY_ARRAY(obj->offsets, src->offsets, src->offset_len);
+ if (src->offset_len) {
+ if (sizeof(*src->offsets) > SIZE_MAX / src->offset_len)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+
+ REFTABLE_ALLOC_ARRAY(obj->offsets, src->offset_len);
+ if (!obj->offsets)
+ return REFTABLE_OUT_OF_MEMORY_ERROR;
+
+ memcpy(obj->offsets, src->offsets, sizeof(*src->offsets) * src->offset_len);
+ obj->offset_len = src->offset_len;
+ }
return 0;
}
@@ -522,7 +525,7 @@ static uint8_t reftable_obj_record_val_type(const void *rec)
}
static int reftable_obj_record_encode(const void *rec, struct string_view s,
- uint32_t hash_size UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED)
{
const struct reftable_obj_record *r = rec;
struct string_view start = s;
@@ -531,24 +534,22 @@ static int reftable_obj_record_encode(const void *rec, struct string_view s,
uint64_t last = 0;
if (r->offset_len == 0 || r->offset_len >= 8) {
n = put_var_int(&s, r->offset_len);
- if (n < 0) {
- return -1;
- }
+ if (n < 0)
+ return n;
string_view_consume(&s, n);
}
if (r->offset_len == 0)
return start.len - s.len;
n = put_var_int(&s, r->offsets[0]);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
last = r->offsets[0];
for (i = 1; i < r->offset_len; i++) {
int n = put_var_int(&s, r->offsets[i] - last);
- if (n < 0) {
- return -1;
- }
+ if (n < 0)
+ return n;
string_view_consume(&s, n);
last = r->offsets[i];
}
@@ -557,8 +558,8 @@ static int reftable_obj_record_encode(const void *rec, struct string_view s,
static int reftable_obj_record_decode(void *rec, struct reftable_buf key,
uint8_t val_type, struct string_view in,
- uint32_t hash_size UNUSED,
- struct reftable_buf *scratch UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED,
+ struct reftable_buf *scratch REFTABLE_UNUSED)
{
struct string_view start = in;
struct reftable_obj_record *r = rec;
@@ -612,13 +613,13 @@ static int reftable_obj_record_decode(void *rec, struct reftable_buf key,
return start.len - in.len;
}
-static int not_a_deletion(const void *p UNUSED)
+static int not_a_deletion(const void *p REFTABLE_UNUSED)
{
return 0;
}
static int reftable_obj_record_equal_void(const void *a, const void *b,
- uint32_t hash_size UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED)
{
struct reftable_obj_record *ra = (struct reftable_obj_record *) a;
struct reftable_obj_record *rb = (struct reftable_obj_record *) b;
@@ -683,7 +684,7 @@ static int reftable_log_record_key(const void *r, struct reftable_buf *dest)
return err;
ts = (~ts) - rec->update_index;
- put_be64(&i64[0], ts);
+ reftable_put_be64(&i64[0], ts);
err = reftable_buf_add(dest, i64, sizeof(i64));
if (err < 0)
@@ -783,7 +784,7 @@ static int reftable_log_record_encode(const void *rec, struct string_view s,
return 0;
if (s.len < 2 * hash_size)
- return -1;
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
memcpy(s.buf, r->value.update.old_hash, hash_size);
memcpy(s.buf + hash_size, r->value.update.new_hash, hash_size);
@@ -791,30 +792,30 @@ static int reftable_log_record_encode(const void *rec, struct string_view s,
n = encode_string(r->value.update.name ? r->value.update.name : "", s);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
n = encode_string(r->value.update.email ? r->value.update.email : "",
s);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
n = put_var_int(&s, r->value.update.time);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
if (s.len < 2)
- return -1;
+ return REFTABLE_ENTRY_TOO_BIG_ERROR;
- put_be16(s.buf, r->value.update.tz_offset);
+ reftable_put_be16(s.buf, r->value.update.tz_offset);
string_view_consume(&s, 2);
n = encode_string(
r->value.update.message ? r->value.update.message : "", s);
if (n < 0)
- return -1;
+ return n;
string_view_consume(&s, n);
return start.len - s.len;
@@ -840,7 +841,7 @@ static int reftable_log_record_decode(void *rec, struct reftable_buf key,
}
memcpy(r->refname, key.buf, key.len - 8);
- ts = get_be64(key.buf + key.len - 8);
+ ts = reftable_get_be64((unsigned char *)key.buf + key.len - 8);
r->update_index = (~max) - ts;
@@ -931,7 +932,7 @@ static int reftable_log_record_decode(void *rec, struct reftable_buf key,
goto done;
}
- r->value.update.tz_offset = get_be16(in.buf);
+ r->value.update.tz_offset = reftable_get_be16(in.buf);
string_view_consume(&in, 2);
n = decode_string(scratch, in);
@@ -1048,7 +1049,7 @@ static int reftable_index_record_key(const void *r, struct reftable_buf *dest)
}
static int reftable_index_record_copy_from(void *rec, const void *src_rec,
- uint32_t hash_size UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED)
{
struct reftable_index_record *dst = rec;
const struct reftable_index_record *src = src_rec;
@@ -1069,13 +1070,13 @@ static void reftable_index_record_release(void *rec)
reftable_buf_release(&idx->last_key);
}
-static uint8_t reftable_index_record_val_type(const void *rec UNUSED)
+static uint8_t reftable_index_record_val_type(const void *rec REFTABLE_UNUSED)
{
return 0;
}
static int reftable_index_record_encode(const void *rec, struct string_view out,
- uint32_t hash_size UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED)
{
const struct reftable_index_record *r =
(const struct reftable_index_record *)rec;
@@ -1091,10 +1092,10 @@ static int reftable_index_record_encode(const void *rec, struct string_view out,
}
static int reftable_index_record_decode(void *rec, struct reftable_buf key,
- uint8_t val_type UNUSED,
+ uint8_t val_type REFTABLE_UNUSED,
struct string_view in,
- uint32_t hash_size UNUSED,
- struct reftable_buf *scratch UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED,
+ struct reftable_buf *scratch REFTABLE_UNUSED)
{
struct string_view start = in;
struct reftable_index_record *r = rec;
@@ -1114,7 +1115,7 @@ static int reftable_index_record_decode(void *rec, struct reftable_buf key,
}
static int reftable_index_record_equal(const void *a, const void *b,
- uint32_t hash_size UNUSED)
+ uint32_t hash_size REFTABLE_UNUSED)
{
struct reftable_index_record *ia = (struct reftable_index_record *) a;
struct reftable_index_record *ib = (struct reftable_index_record *) b;
@@ -1189,12 +1190,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)
+int reftable_record_cmp(struct reftable_record *a, struct reftable_record *b,
+ int *cmp)
{
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));
+ return -1;
+ *cmp = reftable_record_vtable(a)->cmp(reftable_record_data(a),
+ reftable_record_data(b));
+ return 0;
}
int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, uint32_t hash_size)
@@ -1300,7 +1303,7 @@ reftable_record_vtable(struct reftable_record *rec)
abort();
}
-void reftable_record_init(struct reftable_record *rec, uint8_t typ)
+int reftable_record_init(struct reftable_record *rec, uint8_t typ)
{
memset(rec, 0, sizeof(*rec));
rec->type = typ;
@@ -1309,11 +1312,11 @@ void reftable_record_init(struct reftable_record *rec, uint8_t typ)
case BLOCK_TYPE_REF:
case BLOCK_TYPE_LOG:
case BLOCK_TYPE_OBJ:
- return;
+ return 0;
case BLOCK_TYPE_INDEX:
reftable_buf_init(&rec->u.idx.last_key);
- return;
+ return 0;
default:
- BUG("unhandled record type");
+ return REFTABLE_API_ERROR;
}
}
diff --git a/reftable/record.h b/reftable/record.h
index c7755a4d75..867810a932 100644
--- a/reftable/record.h
+++ b/reftable/record.h
@@ -130,11 +130,11 @@ struct reftable_record {
} u;
};
-/* Initialize the reftable record for the given type */
-void reftable_record_init(struct reftable_record *rec, uint8_t typ);
+/* Initialize the reftable record for the given type. */
+int 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_cmp(struct reftable_record *a, struct reftable_record *b, int *cmp);
int reftable_record_equal(struct reftable_record *a, struct reftable_record *b, uint32_t hash_size);
int reftable_record_key(struct reftable_record *rec, struct reftable_buf *dest);
int reftable_record_copy_from(struct reftable_record *rec,
diff --git a/reftable/stack.c b/reftable/stack.c
index 6c4e8be19b..6dac015b47 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -48,6 +48,25 @@ static int stack_fsync(const struct reftable_write_options *opts, int fd)
return fsync(fd);
}
+static ssize_t reftable_write_data(int fd, const void *data, size_t size)
+{
+ size_t total_written = 0;
+ const char *p = data;
+
+ while (total_written < size) {
+ ssize_t bytes_written = write(fd, p, size - total_written);
+ if (bytes_written < 0 && (errno == EAGAIN || errno == EINTR))
+ continue;
+ if (bytes_written < 0)
+ return REFTABLE_IO_ERROR;
+
+ total_written += bytes_written;
+ p += bytes_written;
+ }
+
+ return total_written;
+}
+
struct fd_writer {
const struct reftable_write_options *opts;
int fd;
@@ -56,7 +75,7 @@ struct fd_writer {
static ssize_t fd_writer_write(void *arg, const void *data, size_t sz)
{
struct fd_writer *writer = arg;
- return write_in_full(writer->fd, data, sz);
+ return reftable_write_data(writer->fd, data, sz);
}
static int fd_writer_flush(void *arg)
@@ -115,13 +134,16 @@ out:
static int fd_read_lines(int fd, char ***namesp)
{
- off_t size = lseek(fd, 0, SEEK_END);
char *buf = NULL;
int err = 0;
+ off_t size;
+
+ size = lseek(fd, 0, SEEK_END);
if (size < 0) {
err = REFTABLE_IO_ERROR;
goto done;
}
+
err = lseek(fd, 0, SEEK_SET);
if (err < 0) {
err = REFTABLE_IO_ERROR;
@@ -134,9 +156,16 @@ static int fd_read_lines(int fd, char ***namesp)
goto done;
}
- if (read_in_full(fd, buf, size) != size) {
- err = REFTABLE_IO_ERROR;
- goto done;
+ for (off_t total_read = 0; total_read < size; ) {
+ ssize_t bytes_read = read(fd, buf + total_read, size - total_read);
+ if (bytes_read < 0 && (errno == EAGAIN || errno == EINTR))
+ continue;
+ if (bytes_read < 0 || !bytes_read) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
+ }
+
+ total_read += bytes_read;
}
buf[size] = 0;
@@ -494,8 +523,8 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
close(fd);
fd = -1;
- delay = delay + (delay * git_rand(CSPRNG_BYTES_INSECURE)) / UINT32_MAX + 1;
- sleep_millisec(delay);
+ delay = delay + (delay * reftable_rand()) / UINT32_MAX + 1;
+ poll(NULL, 0, delay);
}
out:
@@ -659,7 +688,7 @@ int reftable_stack_add(struct reftable_stack *st,
static int format_name(struct reftable_buf *dest, uint64_t min, uint64_t max)
{
char buf[100];
- uint32_t rnd = git_rand(CSPRNG_BYTES_INSECURE);
+ uint32_t rnd = reftable_rand();
snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x",
min, max, rnd);
reftable_buf_reset(dest);
@@ -774,7 +803,8 @@ int reftable_addition_commit(struct reftable_addition *add)
goto done;
}
- err = write_in_full(add->tables_list_lock.fd, table_list.buf, table_list.len);
+ err = reftable_write_data(add->tables_list_lock.fd,
+ table_list.buf, table_list.len);
reftable_buf_release(&table_list);
if (err < 0) {
err = REFTABLE_IO_ERROR;
@@ -1460,8 +1490,8 @@ static int stack_compact_range(struct reftable_stack *st,
goto done;
}
- err = write_in_full(tables_list_lock.fd,
- tables_list_buf.buf, tables_list_buf.len);
+ err = reftable_write_data(tables_list_lock.fd,
+ tables_list_buf.buf, tables_list_buf.len);
if (err < 0) {
err = REFTABLE_IO_ERROR;
unlink(new_table_path.buf);
diff --git a/reftable/system.c b/reftable/system.c
index adf8e4d30b..1ee268b125 100644
--- a/reftable/system.c
+++ b/reftable/system.c
@@ -1,9 +1,16 @@
+#include "../git-compat-util.h"
+
#include "system.h"
#include "basics.h"
#include "reftable-error.h"
#include "../lockfile.h"
#include "../tempfile.h"
+uint32_t reftable_rand(void)
+{
+ return git_rand(CSPRNG_BYTES_INSECURE);
+}
+
int tmpfile_from_pattern(struct reftable_tmpfile *out, const char *pattern)
{
struct tempfile *tempfile;
diff --git a/reftable/system.h b/reftable/system.h
index d02eacea8f..072d9daea0 100644
--- a/reftable/system.h
+++ b/reftable/system.h
@@ -11,10 +11,17 @@ https://developers.google.com/open-source/licenses/bsd
/* This header glues the reftable library to the rest of Git */
-#include "git-compat-util.h"
+#define MINGW_DONT_HANDLE_IN_USE_ERROR
+#include "compat/posix.h"
#include "compat/zlib-compat.h"
/*
+ * Return a random 32 bit integer. This function is expected to return
+ * pre-seeded data.
+ */
+uint32_t reftable_rand(void);
+
+/*
* An implementation-specific temporary file. By making this specific to the
* implementation it becomes possible to tie temporary files into any kind of
* signal or atexit handlers for cleanup on abnormal situations.
diff --git a/reftable/writer.c b/reftable/writer.c
index f3ab1035d6..075ea8661b 100644
--- a/reftable/writer.c
+++ b/reftable/writer.c
@@ -99,9 +99,9 @@ static int writer_write_header(struct reftable_writer *w, uint8_t *dest)
dest[4] = writer_version(w);
- put_be24(dest + 5, w->opts.block_size);
- put_be64(dest + 8, w->min_update_index);
- put_be64(dest + 16, w->max_update_index);
+ reftable_put_be24(dest + 5, w->opts.block_size);
+ reftable_put_be64(dest + 8, w->min_update_index);
+ reftable_put_be64(dest + 16, w->max_update_index);
if (writer_version(w) == 2) {
uint32_t hash_id;
@@ -116,7 +116,7 @@ static int writer_write_header(struct reftable_writer *w, uint8_t *dest)
return -1;
}
- put_be32(dest + 24, hash_id);
+ reftable_put_be32(dest + 24, hash_id);
}
return header_size(writer_version(w));
@@ -158,7 +158,7 @@ int reftable_writer_new(struct reftable_writer **out,
opts = *_opts;
options_set_defaults(&opts);
if (opts.block_size >= (1 << 24))
- BUG("configured block size exceeds 16MB");
+ return REFTABLE_API_ERROR;
reftable_buf_init(&wp->block_writer_data.last_key);
reftable_buf_init(&wp->last_key);
@@ -302,19 +302,19 @@ static int writer_add_record(struct reftable_writer *w,
}
if (block_writer_type(w->block_writer) != reftable_record_type(rec))
- BUG("record of type %d added to writer of type %d",
- reftable_record_type(rec), block_writer_type(w->block_writer));
+ return REFTABLE_API_ERROR;
/*
* Try to add the record to the writer. If this succeeds then we're
* done. Otherwise the block writer may have hit the block size limit
* and needs to be flushed.
*/
- if (!block_writer_add(w->block_writer, rec)) {
- err = 0;
+ err = block_writer_add(w->block_writer, rec);
+ if (err == 0)
goto done;
- }
+ if (err != REFTABLE_ENTRY_TOO_BIG_ERROR)
+ goto done;
/*
* The current block is full, so we need to flush and reinitialize the
* writer to start writing the next block.
@@ -329,16 +329,10 @@ static int writer_add_record(struct reftable_writer *w,
/*
* Try to add the record to the writer again. If this still fails then
* the record does not fit into the block size.
- *
- * TODO: it would be great to have `block_writer_add()` return proper
- * error codes so that we don't have to second-guess the failure
- * mode here.
*/
err = block_writer_add(w->block_writer, rec);
- if (err) {
- err = REFTABLE_ENTRY_TOO_BIG_ERROR;
+ if (err)
goto done;
- }
done:
return err;
@@ -625,10 +619,22 @@ static void write_object_record(void *void_arg, void *key)
if (arg->err < 0)
goto done;
+ /*
+ * Try to add the record to the writer. If this succeeds then we're
+ * done. Otherwise the block writer may have hit the block size limit
+ * and needs to be flushed.
+ */
arg->err = block_writer_add(arg->w->block_writer, &rec);
if (arg->err == 0)
goto done;
+ if (arg->err != REFTABLE_ENTRY_TOO_BIG_ERROR)
+ goto done;
+
+ /*
+ * The current block is full, so we need to flush and reinitialize the
+ * writer to start writing the next block.
+ */
arg->err = writer_flush_block(arg->w);
if (arg->err < 0)
goto done;
@@ -637,10 +643,17 @@ static void write_object_record(void *void_arg, void *key)
if (arg->err < 0)
goto done;
+ /*
+ * If this still fails then we may need to reset record's offset
+ * length to reduce the data size to be written.
+ */
arg->err = block_writer_add(arg->w->block_writer, &rec);
if (arg->err == 0)
goto done;
+ if (arg->err != REFTABLE_ENTRY_TOO_BIG_ERROR)
+ goto done;
+
rec.u.obj.offset_len = 0;
arg->err = block_writer_add(arg->w->block_writer, &rec);
@@ -650,7 +663,7 @@ static void write_object_record(void *void_arg, void *key)
done:;
}
-static void object_record_free(void *void_arg UNUSED, void *key)
+static void object_record_free(void *void_arg REFTABLE_UNUSED, void *key)
{
struct obj_index_tree_node *entry = key;
@@ -731,19 +744,19 @@ int reftable_writer_close(struct reftable_writer *w)
}
p += writer_write_header(w, footer);
- put_be64(p, w->stats.ref_stats.index_offset);
+ reftable_put_be64(p, w->stats.ref_stats.index_offset);
p += 8;
- put_be64(p, (w->stats.obj_stats.offset) << 5 | w->stats.object_id_len);
+ reftable_put_be64(p, (w->stats.obj_stats.offset) << 5 | w->stats.object_id_len);
p += 8;
- put_be64(p, w->stats.obj_stats.index_offset);
+ reftable_put_be64(p, w->stats.obj_stats.index_offset);
p += 8;
- put_be64(p, w->stats.log_stats.offset);
+ reftable_put_be64(p, w->stats.log_stats.offset);
p += 8;
- put_be64(p, w->stats.log_stats.index_offset);
+ reftable_put_be64(p, w->stats.log_stats.index_offset);
p += 8;
- put_be32(p, crc32(0, footer, p - footer));
+ reftable_put_be32(p, crc32(0, footer, p - footer));
p += 4;
err = w->flush(w->write_arg);
diff --git a/remote.c b/remote.c
index c9c1384c1d..25af97a44b 100644
--- a/remote.c
+++ b/remote.c
@@ -143,8 +143,8 @@ static struct remote *make_remote(struct remote_state *remote_state,
ret->prune = -1; /* unspecified */
ret->prune_tags = -1; /* unspecified */
ret->name = xstrndup(name, len);
- refspec_init(&ret->push, REFSPEC_PUSH);
- refspec_init(&ret->fetch, REFSPEC_FETCH);
+ refspec_init_push(&ret->push);
+ refspec_init_fetch(&ret->fetch);
string_list_init_dup(&ret->server_options);
ALLOC_GROW(remote_state->remotes, remote_state->remotes_nr + 1,
@@ -321,10 +321,11 @@ static void read_remotes_file(struct remote_state *remote_state,
struct remote *remote)
{
struct strbuf buf = STRBUF_INIT;
- FILE *f = fopen_or_warn(git_path("remotes/%s", remote->name), "r");
+ FILE *f = fopen_or_warn(repo_git_path_append(the_repository, &buf,
+ "remotes/%s", remote->name), "r");
if (!f)
- return;
+ goto out;
warn_about_deprecated_remote_type("remotes", remote);
@@ -343,8 +344,10 @@ static void read_remotes_file(struct remote_state *remote_state,
else if (skip_prefix(buf.buf, "Pull:", &v))
refspec_append(&remote->fetch, skip_spaces(v));
}
- strbuf_release(&buf);
fclose(f);
+
+out:
+ strbuf_release(&buf);
}
static void read_branches_file(struct remote_state *remote_state,
@@ -352,20 +355,19 @@ static void read_branches_file(struct remote_state *remote_state,
{
char *frag, *to_free = NULL;
struct strbuf buf = STRBUF_INIT;
- FILE *f = fopen_or_warn(git_path("branches/%s", remote->name), "r");
+ FILE *f = fopen_or_warn(repo_git_path_append(the_repository, &buf,
+ "branches/%s", remote->name), "r");
if (!f)
- return;
+ goto out;
warn_about_deprecated_remote_type("branches", remote);
strbuf_getline_lf(&buf, f);
fclose(f);
strbuf_trim(&buf);
- if (!buf.len) {
- strbuf_release(&buf);
- return;
- }
+ if (!buf.len)
+ goto out;
remote->configured_in_repo = 1;
remote->origin = REMOTE_BRANCHES;
@@ -393,6 +395,7 @@ static void read_branches_file(struct remote_state *remote_state,
refspec_appendf(&remote->push, "HEAD:refs/heads/%s", frag);
remote->fetch_tags = 1; /* always auto-follow */
+out:
strbuf_release(&buf);
free(to_free);
}
diff --git a/repo-settings.c b/repo-settings.c
index 9d16d5399e..67e9cfd2e6 100644
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -4,6 +4,7 @@
#include "repository.h"
#include "midx.h"
#include "pack-objects.h"
+#include "setup.h"
static void repo_cfg_bool(struct repository *r, const char *key, int *dest,
int def)
@@ -21,7 +22,6 @@ static void repo_cfg_int(struct repository *r, const char *key, int *dest,
void prepare_repo_settings(struct repository *r)
{
- const struct repo_settings defaults = REPO_SETTINGS_INIT;
int experimental;
int value;
const char *strval;
@@ -35,7 +35,7 @@ void prepare_repo_settings(struct repository *r)
if (r->settings.initialized)
return;
- memcpy(&r->settings, &defaults, sizeof(defaults));
+ repo_settings_clear(r);
r->settings.initialized++;
/* Booleans config or default, cascades to other settings */
@@ -143,6 +143,14 @@ void prepare_repo_settings(struct repository *r)
r->settings.packed_git_limit = ulongval;
}
+void repo_settings_clear(struct repository *r)
+{
+ struct repo_settings empty = REPO_SETTINGS_INIT;
+ FREE_AND_NULL(r->settings.fsmonitor);
+ FREE_AND_NULL(r->settings.hooks_path);
+ r->settings = empty;
+}
+
enum log_refs_config repo_settings_get_log_all_ref_updates(struct repository *repo)
{
const char *value;
@@ -167,3 +175,35 @@ int repo_settings_get_warn_ambiguous_refs(struct repository *repo)
&repo->settings.warn_ambiguous_refs, 1);
return repo->settings.warn_ambiguous_refs;
}
+
+const char *repo_settings_get_hooks_path(struct repository *repo)
+{
+ if (!repo->settings.hooks_path)
+ repo_config_get_pathname(repo, "core.hookspath", &repo->settings.hooks_path);
+ return repo->settings.hooks_path;
+}
+
+int repo_settings_get_shared_repository(struct repository *repo)
+{
+ if (!repo->settings.shared_repository_initialized) {
+ const char *var = "core.sharedrepository";
+ const char *value;
+ if (!repo_config_get_value(repo, var, &value))
+ repo->settings.shared_repository = git_config_perm(var, value);
+ else
+ repo->settings.shared_repository = PERM_UMASK;
+ repo->settings.shared_repository_initialized = 1;
+ }
+ return repo->settings.shared_repository;
+}
+
+void repo_settings_set_shared_repository(struct repository *repo, int value)
+{
+ repo->settings.shared_repository = value;
+ repo->settings.shared_repository_initialized = 1;
+}
+
+void repo_settings_reset_shared_repository(struct repository *repo)
+{
+ repo->settings.shared_repository_initialized = 0;
+}
diff --git a/repo-settings.h b/repo-settings.h
index 93ea0c3274..ddc11967e0 100644
--- a/repo-settings.h
+++ b/repo-settings.h
@@ -37,6 +37,9 @@ struct repo_settings {
int pack_use_bitmap_boundary_traversal;
int pack_use_multi_pack_reuse;
+ int shared_repository;
+ int shared_repository_initialized;
+
/*
* Does this repository have core.useReplaceRefs=true (on by
* default)? This provides a repository-scoped version of this
@@ -61,8 +64,11 @@ struct repo_settings {
size_t delta_base_cache_limit;
size_t packed_git_window_size;
size_t packed_git_limit;
+
+ char *hooks_path;
};
#define REPO_SETTINGS_INIT { \
+ .shared_repository = -1, \
.index_version = -1, \
.core_untracked_cache = UNTRACKED_CACHE_KEEP, \
.fetch_negotiation_algorithm = FETCH_NEGOTIATION_CONSECUTIVE, \
@@ -73,10 +79,18 @@ struct repo_settings {
}
void prepare_repo_settings(struct repository *r);
+void repo_settings_clear(struct repository *r);
/* Read the value for "core.logAllRefUpdates". */
enum log_refs_config repo_settings_get_log_all_ref_updates(struct repository *repo);
/* Read the value for "core.warnAmbiguousRefs". */
int repo_settings_get_warn_ambiguous_refs(struct repository *repo);
+/* Read the value for "core.hooksPath". */
+const char *repo_settings_get_hooks_path(struct repository *repo);
+
+/* Read, set or reset the value for "core.sharedRepository". */
+int repo_settings_get_shared_repository(struct repository *repo);
+void repo_settings_set_shared_repository(struct repository *repo, int value);
+void repo_settings_reset_shared_repository(struct repository *repo);
#endif /* REPO_SETTINGS_H */
diff --git a/repository.c b/repository.c
index 1a6a62bbd0..6cbaf2e3da 100644
--- a/repository.c
+++ b/repository.c
@@ -312,8 +312,8 @@ int repo_submodule_init(struct repository *subrepo,
struct strbuf worktree = STRBUF_INIT;
int ret = 0;
- strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", path);
- strbuf_repo_worktree_path(&worktree, superproject, "%s", path);
+ repo_worktree_path_append(superproject, &gitdir, "%s/.git", path);
+ repo_worktree_path_append(superproject, &worktree, "%s", path);
if (repo_init(subrepo, gitdir.buf, worktree.buf)) {
/*
@@ -380,7 +380,7 @@ void repo_clear(struct repository *repo)
parsed_object_pool_clear(repo->parsed_objects);
FREE_AND_NULL(repo->parsed_objects);
- FREE_AND_NULL(repo->settings.fsmonitor);
+ repo_settings_clear(repo);
if (repo->config) {
git_configset_clear(repo->config);
diff --git a/rerere.c b/rerere.c
index c42cee618b..740e8ad1a0 100644
--- a/rerere.c
+++ b/rerere.c
@@ -91,16 +91,18 @@ static void assign_variant(struct rerere_id *id)
id->variant = variant;
}
-const char *rerere_path(const struct rerere_id *id, const char *file)
+const char *rerere_path(struct strbuf *buf, const struct rerere_id *id, const char *file)
{
if (!file)
- return git_path("rr-cache/%s", rerere_id_hex(id));
+ return repo_git_path_replace(the_repository, buf, "rr-cache/%s",
+ rerere_id_hex(id));
if (id->variant <= 0)
- return git_path("rr-cache/%s/%s", rerere_id_hex(id), file);
+ return repo_git_path_replace(the_repository, buf, "rr-cache/%s/%s",
+ rerere_id_hex(id), file);
- return git_path("rr-cache/%s/%s.%d",
- rerere_id_hex(id), file, id->variant);
+ return repo_git_path_replace(the_repository, buf, "rr-cache/%s/%s.%d",
+ rerere_id_hex(id), file, id->variant);
}
static int is_rr_file(const char *name, const char *filename, int *variant)
@@ -125,8 +127,12 @@ static int is_rr_file(const char *name, const char *filename, int *variant)
static void scan_rerere_dir(struct rerere_dir *rr_dir)
{
struct dirent *de;
- DIR *dir = opendir(git_path("rr-cache/%s", rr_dir->name));
+ char *path;
+ DIR *dir;
+ path = repo_git_path(the_repository, "rr-cache/%s", rr_dir->name);
+ dir = opendir(path);
+ free(path);
if (!dir)
return;
while ((de = readdir(dir)) != NULL) {
@@ -624,9 +630,10 @@ static int try_merge(struct index_state *istate,
{
enum ll_merge_result ret;
mmfile_t base = {NULL, 0}, other = {NULL, 0};
+ struct strbuf buf = STRBUF_INIT;
- if (read_mmfile(&base, rerere_path(id, "preimage")) ||
- read_mmfile(&other, rerere_path(id, "postimage"))) {
+ if (read_mmfile(&base, rerere_path(&buf, id, "preimage")) ||
+ read_mmfile(&other, rerere_path(&buf, id, "postimage"))) {
ret = LL_MERGE_CONFLICT;
} else {
/*
@@ -637,6 +644,7 @@ static int try_merge(struct index_state *istate,
istate, NULL);
}
+ strbuf_release(&buf);
free(base.ptr);
free(other.ptr);
@@ -657,6 +665,7 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
{
FILE *f;
int ret;
+ struct strbuf buf = STRBUF_INIT;
mmfile_t cur = {NULL, 0};
mmbuffer_t result = {NULL, 0};
@@ -664,8 +673,8 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
* Normalize the conflicts in path and write it out to
* "thisimage" temporary file.
*/
- if ((handle_file(istate, path, NULL, rerere_path(id, "thisimage")) < 0) ||
- read_mmfile(&cur, rerere_path(id, "thisimage"))) {
+ if ((handle_file(istate, path, NULL, rerere_path(&buf, id, "thisimage")) < 0) ||
+ read_mmfile(&cur, rerere_path(&buf, id, "thisimage"))) {
ret = 1;
goto out;
}
@@ -678,9 +687,9 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
* A successful replay of recorded resolution.
* Mark that "postimage" was used to help gc.
*/
- if (utime(rerere_path(id, "postimage"), NULL) < 0)
+ if (utime(rerere_path(&buf, id, "postimage"), NULL) < 0)
warning_errno(_("failed utime() on '%s'"),
- rerere_path(id, "postimage"));
+ rerere_path(&buf, id, "postimage"));
/* Update "path" with the resolution */
f = fopen(path, "w");
@@ -694,6 +703,7 @@ static int merge(struct index_state *istate, const struct rerere_id *id, const c
out:
free(cur.ptr);
free(result.ptr);
+ strbuf_release(&buf);
return ret;
}
@@ -720,9 +730,11 @@ static void update_paths(struct repository *r, struct string_list *update)
static void remove_variant(struct rerere_id *id)
{
- unlink_or_warn(rerere_path(id, "postimage"));
- unlink_or_warn(rerere_path(id, "preimage"));
+ struct strbuf buf = STRBUF_INIT;
+ unlink_or_warn(rerere_path(&buf, id, "postimage"));
+ unlink_or_warn(rerere_path(&buf, id, "preimage"));
id->collection->status[id->variant] = 0;
+ strbuf_release(&buf);
}
/*
@@ -739,6 +751,7 @@ static void do_rerere_one_path(struct index_state *istate,
const char *path = rr_item->string;
struct rerere_id *id = rr_item->util;
struct rerere_dir *rr_dir = id->collection;
+ struct strbuf buf = STRBUF_INIT;
int variant;
variant = id->variant;
@@ -746,12 +759,12 @@ static void do_rerere_one_path(struct index_state *istate,
/* Has the user resolved it already? */
if (variant >= 0) {
if (!handle_file(istate, path, NULL, NULL)) {
- copy_file(rerere_path(id, "postimage"), path, 0666);
+ copy_file(rerere_path(&buf, id, "postimage"), path, 0666);
id->collection->status[variant] |= RR_HAS_POSTIMAGE;
fprintf_ln(stderr, _("Recorded resolution for '%s'."), path);
free_rerere_id(rr_item);
rr_item->util = NULL;
- return;
+ goto out;
}
/*
* There may be other variants that can cleanly
@@ -787,22 +800,25 @@ static void do_rerere_one_path(struct index_state *istate,
path);
free_rerere_id(rr_item);
rr_item->util = NULL;
- return;
+ goto out;
}
/* None of the existing one applies; we need a new variant */
assign_variant(id);
variant = id->variant;
- handle_file(istate, path, NULL, rerere_path(id, "preimage"));
+ handle_file(istate, path, NULL, rerere_path(&buf, id, "preimage"));
if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
- const char *path = rerere_path(id, "postimage");
+ const char *path = rerere_path(&buf, id, "postimage");
if (unlink(path))
die_errno(_("cannot unlink stray '%s'"), path);
id->collection->status[variant] &= ~RR_HAS_POSTIMAGE;
}
id->collection->status[variant] |= RR_HAS_PREIMAGE;
fprintf_ln(stderr, _("Recorded preimage for '%s'"), path);
+
+out:
+ strbuf_release(&buf);
}
static int do_plain_rerere(struct repository *r,
@@ -810,6 +826,7 @@ static int do_plain_rerere(struct repository *r,
{
struct string_list conflict = STRING_LIST_INIT_DUP;
struct string_list update = STRING_LIST_INIT_DUP;
+ struct strbuf buf = STRBUF_INIT;
int i;
find_conflict(r, &conflict);
@@ -843,7 +860,7 @@ static int do_plain_rerere(struct repository *r,
string_list_insert(rr, path)->util = id;
/* Ensure that the directory exists. */
- mkdir_in_gitdir(rerere_path(id, NULL));
+ mkdir_in_gitdir(rerere_path(&buf, id, NULL));
}
for (i = 0; i < rr->nr; i++)
@@ -854,6 +871,7 @@ static int do_plain_rerere(struct repository *r,
string_list_clear(&conflict, 0);
string_list_clear(&update, 0);
+ strbuf_release(&buf);
return write_rr(rr, fd);
}
@@ -1033,6 +1051,7 @@ static int rerere_forget_one_path(struct index_state *istate,
struct rerere_id *id;
unsigned char hash[GIT_MAX_RAWSZ];
int ret;
+ struct strbuf buf = STRBUF_INIT;
struct string_list_item *item;
/*
@@ -1056,8 +1075,8 @@ static int rerere_forget_one_path(struct index_state *istate,
if (!has_rerere_resolution(id))
continue;
- handle_cache(istate, path, hash, rerere_path(id, "thisimage"));
- if (read_mmfile(&cur, rerere_path(id, "thisimage"))) {
+ handle_cache(istate, path, hash, rerere_path(&buf, id, "thisimage"));
+ if (read_mmfile(&cur, rerere_path(&buf, id, "thisimage"))) {
free(cur.ptr);
error(_("failed to update conflicted state in '%s'"), path);
goto fail_exit;
@@ -1074,7 +1093,7 @@ static int rerere_forget_one_path(struct index_state *istate,
goto fail_exit;
}
- filename = rerere_path(id, "postimage");
+ filename = rerere_path(&buf, id, "postimage");
if (unlink(filename)) {
if (errno == ENOENT)
error(_("no remembered resolution for '%s'"), path);
@@ -1088,7 +1107,7 @@ static int rerere_forget_one_path(struct index_state *istate,
* conflict in the working tree, run us again to record
* the postimage.
*/
- handle_cache(istate, path, hash, rerere_path(id, "preimage"));
+ handle_cache(istate, path, hash, rerere_path(&buf, id, "preimage"));
fprintf_ln(stderr, _("Updated preimage for '%s'"), path);
/*
@@ -1099,9 +1118,11 @@ static int rerere_forget_one_path(struct index_state *istate,
free_rerere_id(item);
item->util = id;
fprintf(stderr, _("Forgot resolution for '%s'\n"), path);
+ strbuf_release(&buf);
return 0;
fail_exit:
+ strbuf_release(&buf);
free(id);
return -1;
}
@@ -1147,16 +1168,26 @@ int rerere_forget(struct repository *r, struct pathspec *pathspec)
static timestamp_t rerere_created_at(struct rerere_id *id)
{
+ struct strbuf buf = STRBUF_INIT;
struct stat st;
+ timestamp_t ret;
+
+ ret = stat(rerere_path(&buf, id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
- return stat(rerere_path(id, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
+ strbuf_release(&buf);
+ return ret;
}
static timestamp_t rerere_last_used_at(struct rerere_id *id)
{
+ struct strbuf buf = STRBUF_INIT;
struct stat st;
+ timestamp_t ret;
+
+ ret = stat(rerere_path(&buf, id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
- return stat(rerere_path(id, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
+ strbuf_release(&buf);
+ return ret;
}
/*
@@ -1164,9 +1195,11 @@ static timestamp_t rerere_last_used_at(struct rerere_id *id)
*/
static void unlink_rr_item(struct rerere_id *id)
{
- unlink_or_warn(rerere_path(id, "thisimage"));
+ struct strbuf buf = STRBUF_INIT;
+ unlink_or_warn(rerere_path(&buf, id, "thisimage"));
remove_variant(id);
id->collection->status[id->variant] = 0;
+ strbuf_release(&buf);
}
static void prune_one(struct rerere_id *id,
@@ -1205,6 +1238,7 @@ void rerere_gc(struct repository *r, struct string_list *rr)
timestamp_t now = time(NULL);
timestamp_t cutoff_noresolve = now - 15 * 86400;
timestamp_t cutoff_resolve = now - 60 * 86400;
+ struct strbuf buf = STRBUF_INIT;
if (setup_rerere(r, rr, 0) < 0)
return;
@@ -1214,7 +1248,7 @@ void rerere_gc(struct repository *r, struct string_list *rr)
repo_config_get_expiry_in_days(the_repository, "gc.rerereunresolved",
&cutoff_noresolve, now);
git_config(git_default_config, NULL);
- dir = opendir(git_path("rr-cache"));
+ dir = opendir(repo_git_path_replace(the_repository, &buf, "rr-cache"));
if (!dir)
die_errno(_("unable to open rr-cache directory"));
/* Collect stale conflict IDs ... */
@@ -1243,9 +1277,12 @@ void rerere_gc(struct repository *r, struct string_list *rr)
/* ... and then remove the empty directories */
for (i = 0; i < to_remove.nr; i++)
- rmdir(git_path("rr-cache/%s", to_remove.items[i].string));
+ rmdir(repo_git_path_replace(the_repository, &buf,
+ "rr-cache/%s", to_remove.items[i].string));
+
string_list_clear(&to_remove, 0);
rollback_lock_file(&write_lock);
+ strbuf_release(&buf);
}
/*
@@ -1264,10 +1301,14 @@ void rerere_clear(struct repository *r, struct string_list *merge_rr)
for (i = 0; i < merge_rr->nr; i++) {
struct rerere_id *id = merge_rr->items[i].util;
+ struct strbuf buf = STRBUF_INIT;
+
if (!has_rerere_resolution(id)) {
unlink_rr_item(id);
- rmdir(rerere_path(id, NULL));
+ rmdir(rerere_path(&buf, id, NULL));
}
+
+ strbuf_release(&buf);
}
unlink_or_warn(git_path_merge_rr(r));
rollback_lock_file(&write_lock);
diff --git a/rerere.h b/rerere.h
index 5d6cb63879..d4b5f7c932 100644
--- a/rerere.h
+++ b/rerere.h
@@ -32,7 +32,8 @@ int repo_rerere(struct repository *, int);
* path to that filesystem entity. With "file" specified with NULL,
* return the path to the directory that houses these files.
*/
-const char *rerere_path(const struct rerere_id *, const char *file);
+const char *rerere_path(struct strbuf *buf, const struct rerere_id *,
+ const char *file);
int rerere_forget(struct repository *, struct pathspec *);
int rerere_remaining(struct repository *, struct string_list *);
void rerere_clear(struct repository *, struct string_list *);
diff --git a/revision.c b/revision.c
index 474fa1e767..c4390f0938 100644
--- a/revision.c
+++ b/revision.c
@@ -1874,15 +1874,20 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
struct index_state istate = INDEX_STATE_INIT(revs->repo);
+ char *wt_gitdir;
if (wt->is_current)
continue; /* current index already taken care of */
+ wt_gitdir = get_worktree_git_dir(wt);
+
if (read_index_from(&istate,
worktree_git_path(the_repository, wt, "index"),
- get_worktree_git_dir(wt)) > 0)
+ wt_gitdir) > 0)
do_add_index_objects_to_pending(revs, &istate, flags);
+
discard_index(&istate);
+ free(wt_gitdir);
}
free_worktrees(worktrees);
}
diff --git a/run-command.c b/run-command.c
index 402138b8b5..8833b23367 100644
--- a/run-command.c
+++ b/run-command.c
@@ -515,7 +515,13 @@ static void atfork_prepare(struct atfork_state *as)
{
sigset_t all;
- if (sigfillset(&all))
+ /*
+ * POSIX says sigfillset() can fail, but an overly clever
+ * compiler can see through the header files and decide
+ * it cannot fail on a particular platform it is compiling for,
+ * triggering -Wunreachable-code false positive.
+ */
+ if (NOT_CONSTANT(sigfillset(&all)))
die_errno("sigfillset");
#ifdef NO_PTHREADS
if (sigprocmask(SIG_SETMASK, &all, &as->old))
diff --git a/scalar.c b/scalar.c
index da42b4be0c..d359f08bb8 100644
--- a/scalar.c
+++ b/scalar.c
@@ -241,7 +241,7 @@ static int add_or_remove_enlistment(int add)
static int start_fsmonitor_daemon(void)
{
- assert(have_fsmonitor_support());
+ ASSERT(have_fsmonitor_support());
if (fsmonitor_ipc__get_state() != IPC_STATE__LISTENING)
return run_git("fsmonitor--daemon", "start", NULL);
@@ -251,7 +251,7 @@ static int start_fsmonitor_daemon(void)
static int stop_fsmonitor_daemon(void)
{
- assert(have_fsmonitor_support());
+ ASSERT(have_fsmonitor_support());
if (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING)
return run_git("fsmonitor--daemon", "stop", NULL);
diff --git a/sequencer.c b/sequencer.c
index ad0ab75c8d..c625a39111 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4965,7 +4965,7 @@ static int pick_commits(struct repository *r,
ctx->reflog_message = sequencer_reflog_action(opts);
if (opts->allow_ff)
- assert(!(opts->signoff || opts->no_commit ||
+ ASSERT(!(opts->signoff || opts->no_commit ||
opts->record_origin || should_edit(opts) ||
opts->committer_date_is_author_date ||
opts->ignore_date));
diff --git a/serve.c b/serve.c
index f6dfe34a2b..e3ccf1505c 100644
--- a/serve.c
+++ b/serve.c
@@ -10,6 +10,7 @@
#include "upload-pack.h"
#include "bundle-uri.h"
#include "trace2.h"
+#include "promisor-remote.h"
static int advertise_sid = -1;
static int advertise_object_info = -1;
@@ -29,6 +30,26 @@ static int agent_advertise(struct repository *r UNUSED,
return 1;
}
+static int promisor_remote_advertise(struct repository *r,
+ struct strbuf *value)
+{
+ if (value) {
+ char *info = promisor_remote_info(r);
+ if (!info)
+ return 0;
+ strbuf_addstr(value, info);
+ free(info);
+ }
+ return 1;
+}
+
+static void promisor_remote_receive(struct repository *r,
+ const char *remotes)
+{
+ mark_promisor_remotes_as_accepted(r, remotes);
+}
+
+
static int object_format_advertise(struct repository *r,
struct strbuf *value)
{
@@ -155,6 +176,11 @@ static struct protocol_capability capabilities[] = {
.advertise = bundle_uri_advertise,
.command = bundle_uri_command,
},
+ {
+ .name = "promisor-remote",
+ .advertise = promisor_remote_advertise,
+ .receive = promisor_remote_receive,
+ },
};
void protocol_v2_advertise_capabilities(struct repository *r)
diff --git a/server-info.c b/server-info.c
index 31c3fdc118..1ca0e00d51 100644
--- a/server-info.c
+++ b/server-info.c
@@ -125,7 +125,7 @@ static int update_info_file(struct repository *r, char *path,
uic.cur_fp = NULL;
if (uic_is_stale(&uic)) {
- if (adjust_shared_perm(get_tempfile_path(f)) < 0)
+ if (adjust_shared_perm(r, get_tempfile_path(f)) < 0)
goto out;
if (rename_tempfile(&f, path) < 0)
goto out;
diff --git a/setup.c b/setup.c
index 7da7aa8984..f93bd6a24a 100644
--- a/setup.c
+++ b/setup.c
@@ -792,7 +792,7 @@ int upgrade_repository_format(int target_version)
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
int ret;
- strbuf_git_common_path(&sb, the_repository, "config");
+ repo_common_path_append(the_repository, &sb, "config");
read_repository_format(&repo_fmt, sb.buf);
strbuf_release(&sb);
@@ -1822,7 +1822,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
*
* Regardless of the state of nongit_ok, startup_info->prefix and
* the GIT_PREFIX environment variable must always match. For details
- * see Documentation/config/alias.txt.
+ * see Documentation/config/alias.adoc.
*/
if (nongit_ok && *nongit_ok)
startup_info->have_repository = 0;
@@ -2088,7 +2088,7 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path,
* with the way the namespace under .git/ is organized, should
* be really carefully chosen.
*/
- safe_create_dir(path->buf, 1);
+ safe_create_dir(the_repository, path->buf, 1);
while ((de = readdir(dir)) != NULL) {
struct stat st_git, st_template;
int exists = 0;
@@ -2242,7 +2242,7 @@ void initialize_repository_version(int hash_algo,
struct strbuf config = STRBUF_INIT;
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
- strbuf_git_common_path(&config, the_repository, "config");
+ repo_common_path_append(the_repository, &config, "config");
read_repository_format(&repo_fmt, config.buf);
if (repo_fmt.v1_only_extensions.nr)
@@ -2264,7 +2264,7 @@ static int is_reinit(void)
char junk[2];
int ret;
- git_path_buf(&buf, "HEAD");
+ repo_git_path_replace(the_repository, &buf, "HEAD");
ret = !access(buf.buf, R_OK) || readlink(buf.buf, junk, sizeof(junk) - 1) != -1;
strbuf_release(&buf);
return ret;
@@ -2316,8 +2316,7 @@ static int create_default_files(const char *template_path,
int init_shared_repository)
{
struct stat st1;
- struct strbuf buf = STRBUF_INIT;
- char *path;
+ struct strbuf path = STRBUF_INIT;
int reinit;
int filemode;
const char *work_tree = repo_get_work_tree(the_repository);
@@ -2333,7 +2332,7 @@ static int create_default_files(const char *template_path,
*/
copy_templates(template_path);
git_config_clear();
- reset_shared_repository();
+ repo_settings_reset_shared_repository(the_repository);
git_config(git_default_config, NULL);
reinit = is_reinit();
@@ -2343,7 +2342,8 @@ static int create_default_files(const char *template_path,
* values we might have just re-read from the config.
*/
if (init_shared_repository != -1)
- set_shared_repository(init_shared_repository);
+ repo_settings_set_shared_repository(the_repository,
+ init_shared_repository);
is_bare_repository_cfg = !work_tree;
@@ -2351,21 +2351,21 @@ static int create_default_files(const char *template_path,
* We would have created the above under user's umask -- under
* shared-repository settings, we would need to fix them up.
*/
- if (get_shared_repository()) {
- adjust_shared_perm(repo_get_git_dir(the_repository));
+ if (repo_settings_get_shared_repository(the_repository)) {
+ adjust_shared_perm(the_repository, repo_get_git_dir(the_repository));
}
initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, reinit);
/* Check filemode trustability */
- path = git_path_buf(&buf, "config");
+ repo_git_path_replace(the_repository, &path, "config");
filemode = TEST_FILEMODE;
- if (TEST_FILEMODE && !lstat(path, &st1)) {
+ if (TEST_FILEMODE && !lstat(path.buf, &st1)) {
struct stat st2;
- filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
- !lstat(path, &st2) &&
+ filemode = (!chmod(path.buf, st1.st_mode ^ S_IXUSR) &&
+ !lstat(path.buf, &st2) &&
st1.st_mode != st2.st_mode &&
- !chmod(path, st1.st_mode));
+ !chmod(path.buf, st1.st_mode));
if (filemode && !reinit && (st1.st_mode & S_IXUSR))
filemode = 0;
}
@@ -2384,24 +2384,24 @@ static int create_default_files(const char *template_path,
if (!reinit) {
/* Check if symlink is supported in the work tree */
- path = git_path_buf(&buf, "tXXXXXX");
- if (!close(xmkstemp(path)) &&
- !unlink(path) &&
- !symlink("testing", path) &&
- !lstat(path, &st1) &&
+ repo_git_path_replace(the_repository, &path, "tXXXXXX");
+ if (!close(xmkstemp(path.buf)) &&
+ !unlink(path.buf) &&
+ !symlink("testing", path.buf) &&
+ !lstat(path.buf, &st1) &&
S_ISLNK(st1.st_mode))
- unlink(path); /* good */
+ unlink(path.buf); /* good */
else
git_config_set("core.symlinks", "false");
/* Check if the filesystem is case-insensitive */
- path = git_path_buf(&buf, "CoNfIg");
- if (!access(path, F_OK))
+ repo_git_path_replace(the_repository, &path, "CoNfIg");
+ if (!access(path.buf, F_OK))
git_config_set("core.ignorecase", "true");
probe_utf8_pathname_composition();
}
- strbuf_release(&buf);
+ strbuf_release(&path);
return reinit;
}
@@ -2413,15 +2413,15 @@ static void create_object_directory(void)
strbuf_addstr(&path, repo_get_object_directory(the_repository));
baselen = path.len;
- safe_create_dir(path.buf, 1);
+ safe_create_dir(the_repository, path.buf, 1);
strbuf_setlen(&path, baselen);
strbuf_addstr(&path, "/pack");
- safe_create_dir(path.buf, 1);
+ safe_create_dir(the_repository, path.buf, 1);
strbuf_setlen(&path, baselen);
strbuf_addstr(&path, "/info");
- safe_create_dir(path.buf, 1);
+ safe_create_dir(the_repository, path.buf, 1);
strbuf_release(&path);
}
@@ -2592,7 +2592,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
*/
git_config(platform_core_config, NULL);
- safe_create_dir(git_dir, 0);
+ safe_create_dir(the_repository, git_dir, 0);
reinit = create_default_files(template_dir, original_git_dir,
&repo_fmt, init_shared_repository);
@@ -2602,7 +2602,7 @@ int init_db(const char *git_dir, const char *real_git_dir,
initial_branch, flags & INIT_DB_QUIET);
create_object_directory();
- if (get_shared_repository()) {
+ if (repo_settings_get_shared_repository(the_repository)) {
char buf[10];
/* We do not spell "group" and such, so that
* the configuration can be read by older version
@@ -2610,12 +2610,12 @@ int init_db(const char *git_dir, const char *real_git_dir,
* and compatibility values for PERM_GROUP and
* PERM_EVERYBODY.
*/
- if (get_shared_repository() < 0)
+ if (repo_settings_get_shared_repository(the_repository) < 0)
/* force to the mode value */
- xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository());
- else if (get_shared_repository() == PERM_GROUP)
+ xsnprintf(buf, sizeof(buf), "0%o", -repo_settings_get_shared_repository(the_repository));
+ else if (repo_settings_get_shared_repository(the_repository) == PERM_GROUP)
xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP);
- else if (get_shared_repository() == PERM_EVERYBODY)
+ else if (repo_settings_get_shared_repository(the_repository) == PERM_EVERYBODY)
xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY);
else
BUG("invalid value for shared_repository");
@@ -2627,12 +2627,12 @@ int init_db(const char *git_dir, const char *real_git_dir,
int len = strlen(git_dir);
if (reinit)
- printf(get_shared_repository()
+ printf(repo_settings_get_shared_repository(the_repository)
? _("Reinitialized existing shared Git repository in %s%s\n")
: _("Reinitialized existing Git repository in %s%s\n"),
git_dir, len && git_dir[len-1] != '/' ? "/" : "");
else
- printf(get_shared_repository()
+ printf(repo_settings_get_shared_repository(the_repository)
? _("Initialized empty shared Git repository in %s%s\n")
: _("Initialized empty Git repository in %s%s\n"),
git_dir, len && git_dir[len-1] != '/' ? "/" : "");
diff --git a/shallow.c b/shallow.c
index b54244ffa9..4bd9342c9a 100644
--- a/shallow.c
+++ b/shallow.c
@@ -364,7 +364,9 @@ const char *setup_temporary_shallow(const struct oid_array *extra)
struct strbuf sb = STRBUF_INIT;
if (write_shallow_commits(&sb, 0, extra)) {
- temp = xmks_tempfile(git_path("shallow_XXXXXX"));
+ char *path = repo_git_path(the_repository, "shallow_XXXXXX");
+ temp = xmks_tempfile(path);
+ free(path);
if (write_in_full(temp->fd, sb.buf, sb.len) < 0 ||
close_tempfile_gently(temp) < 0)
diff --git a/simple-ipc.h b/simple-ipc.h
index 3916eaf70d..701e005cb8 100644
--- a/simple-ipc.h
+++ b/simple-ipc.h
@@ -2,7 +2,7 @@
#define GIT_SIMPLE_IPC_H
/*
- * See Documentation/technical/api-simple-ipc.txt
+ * See Documentation/technical/api-simple-ipc.adoc
*/
enum ipc_active_state {
diff --git a/submodule.c b/submodule.c
index b361076c5b..0530e8cf24 100644
--- a/submodule.c
+++ b/submodule.c
@@ -536,7 +536,8 @@ static struct repository *open_submodule(const char *path)
struct strbuf sb = STRBUF_INIT;
struct repository *out = xmalloc(sizeof(*out));
- if (submodule_to_gitdir(&sb, path) || repo_init(out, sb.buf, NULL)) {
+ if (submodule_to_gitdir(the_repository, &sb, path) ||
+ repo_init(out, sb.buf, NULL)) {
strbuf_release(&sb);
free(out);
return NULL;
@@ -1315,7 +1316,7 @@ static int repo_has_absorbed_submodules(struct repository *r)
int ret;
struct strbuf buf = STRBUF_INIT;
- strbuf_repo_git_path(&buf, r, "modules/");
+ repo_git_path_append(r, &buf, "modules/");
ret = file_exists(buf.buf) && !is_empty_dir(buf.buf);
strbuf_release(&buf);
return ret;
@@ -2572,7 +2573,8 @@ int get_superproject_working_tree(struct strbuf *buf)
* Put the gitdir for a submodule (given relative to the main
* repository worktree) into `buf`, or return -1 on error.
*/
-int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
+int submodule_to_gitdir(struct repository *repo,
+ struct strbuf *buf, const char *submodule)
{
const struct submodule *sub;
const char *git_dir;
@@ -2592,14 +2594,13 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
strbuf_addstr(buf, git_dir);
}
if (!is_git_directory(buf->buf)) {
- sub = submodule_from_path(the_repository, null_oid(),
- submodule);
+ sub = submodule_from_path(repo, null_oid(), submodule);
if (!sub) {
ret = -1;
goto cleanup;
}
strbuf_reset(buf);
- submodule_name_to_gitdir(buf, the_repository, sub->name);
+ submodule_name_to_gitdir(buf, repo, sub->name);
}
cleanup:
@@ -2629,6 +2630,6 @@ void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r,
* administrators can explicitly set. Nothing has been decided,
* so for now, just append the name at the end of the path.
*/
- strbuf_repo_git_path(buf, r, "modules/");
+ repo_git_path_append(r, buf, "modules/");
strbuf_addstr(buf, submodule_name);
}
diff --git a/submodule.h b/submodule.h
index 4deb1b5f84..db980c1d08 100644
--- a/submodule.h
+++ b/submodule.h
@@ -136,7 +136,8 @@ int push_unpushed_submodules(struct repository *r,
* path of that submodule in 'buf'. Return -1 on error or when the
* submodule is not initialized.
*/
-int submodule_to_gitdir(struct strbuf *buf, const char *submodule);
+int submodule_to_gitdir(struct repository *repo,
+ struct strbuf *buf, const char *submodule);
/*
* Given a submodule name, create a path to where the submodule's gitdir lives
diff --git a/t/README b/t/README
index 53e5b4a710..e9ffd9a81c 100644
--- a/t/README
+++ b/t/README
@@ -818,7 +818,7 @@ Skipping tests
--------------
If you need to skip tests you should do so by using the three-arg form
-of the test_* functions (see the "Test harness library" section
+of the test_expect_* functions (see the "Test harness library" section
below), e.g.:
test_expect_success PERL 'I need Perl' '
@@ -965,6 +965,29 @@ see test-lib-functions.sh for the full list and their options.
test_done
fi
+ - test_lazy_prereq <prereq> <script>
+
+ Declare the way to determine if a test prerequisite <prereq> is
+ satisified or not, but delay the actual determination until the
+ prerequisite is actually used by "test_have_prereq" or the
+ three-arg form of the test_expect_* functions. For example, this
+ is how the SYMLINKS prerequisite is declared to see if the platform
+ supports symbolic links:
+
+ test_lazy_prereq SYMLINKS '
+ ln -s x y && test -h y
+ '
+
+ The script is lazily invoked when SYMLINKS prerequisite is first
+ queried by either "test_have_prereq SYMLINKS" or "test_expect_*
+ SYMLINKS ...". The script is run in a temporary directory inside
+ a subshell, so you do not have to worry about removing temporary
+ files you create there. When the script exits with status 0, the
+ prerequisite is set. Exiting with non-zero status other than 125
+ makes the prerequisite unsatisified. Exiting the script with 125
+ signals a programming error and is used to mark a prerequisite that
+ should not be used by test scripts.
+
- test_expect_code <exit-code> <command>
Run a command and ensure that it exits with the given exit code.
diff --git a/t/helper/test-dir-iterator.c b/t/helper/test-dir-iterator.c
index 6b297bd753..8d46e8ba40 100644
--- a/t/helper/test-dir-iterator.c
+++ b/t/helper/test-dir-iterator.c
@@ -53,6 +53,7 @@ int cmd__dir_iterator(int argc, const char **argv)
printf("(%s) [%s] %s\n", diter->relative_path, diter->basename,
diter->path.buf);
}
+ dir_iterator_free(diter);
if (iter_status != ITER_DONE) {
printf("dir_iterator_advance failure\n");
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
index 72ac8d1b1b..f3c59e5028 100644
--- a/t/helper/test-path-utils.c
+++ b/t/helper/test-path-utils.c
@@ -504,6 +504,25 @@ int cmd__path_utils(int argc, const char **argv)
return !!res;
}
+ if (argc > 1 && !strcmp(argv[1], "is_path_owned_by_current_user")) {
+ int res = 0;
+
+ for (int i = 2; i < argc; i++) {
+ struct strbuf buf = STRBUF_INIT;
+
+ if (is_path_owned_by_current_user(argv[i], &buf))
+ printf("'%s' is owned by current SID\n", argv[i]);
+ else {
+ printf("'%s' is not owned by current SID: %s\n", argv[i], buf.buf);
+ res = 1;
+ }
+
+ strbuf_release(&buf);
+ }
+
+ return res;
+ }
+
fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
argv[1] ? argv[1] : "(there was none)");
return 1;
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 1cc05f043a..e00fce592b 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -75,11 +75,10 @@ static const char **get_store(const char **argv, struct ref_store **refs)
*refs = get_main_ref_store(the_repository);
} else if (skip_prefix(argv[0], "submodule:", &gitdir)) {
struct strbuf sb = STRBUF_INIT;
- int ret;
- ret = strbuf_git_path_submodule(&sb, gitdir, "objects/");
- if (ret)
- die("strbuf_git_path_submodule failed: %d", ret);
+ if (!repo_submodule_path_append(the_repository,
+ &sb, gitdir, "objects/"))
+ die("computing submodule path failed");
add_to_alternates_memory(sb.buf);
strbuf_release(&sb);
diff --git a/t/helper/test-rot13-filter.c b/t/helper/test-rot13-filter.c
index ff407b575c..722b1cbe77 100644
--- a/t/helper/test-rot13-filter.c
+++ b/t/helper/test-rot13-filter.c
@@ -1,6 +1,6 @@
/*
* Example implementation for the Git filter protocol version 2
- * See Documentation/gitattributes.txt, section "Filter Protocol"
+ * See Documentation/gitattributes.adoc, section "Filter Protocol"
*
* Usage: test-tool rot13-filter [--always-delay] --log=<path> <capabilities>
*
diff --git a/t/meson.build b/t/meson.build
index 25ce072707..8b3aed14ea 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -4,6 +4,9 @@ clar_test_suites = [
'unit-tests/u-hash.c',
'unit-tests/u-hashmap.c',
'unit-tests/u-mem-pool.c',
+ 'unit-tests/u-oid-array.c',
+ 'unit-tests/u-oidmap.c',
+ 'unit-tests/u-oidtree.c',
'unit-tests/u-prio-queue.c',
'unit-tests/u-reftable-tree.c',
'unit-tests/u-strbuf.c',
@@ -14,6 +17,7 @@ clar_test_suites = [
clar_sources = [
'unit-tests/clar/clar.c',
'unit-tests/unit-test.c',
+ 'unit-tests/lib-oid.c'
]
clar_decls_h = custom_target(
@@ -48,9 +52,6 @@ clar_unit_tests = executable('unit-tests',
test('unit-tests', clar_unit_tests)
unit_test_programs = [
- 'unit-tests/t-oid-array.c',
- 'unit-tests/t-oidmap.c',
- 'unit-tests/t-oidtree.c',
'unit-tests/t-reftable-basics.c',
'unit-tests/t-reftable-block.c',
'unit-tests/t-reftable-merged.c',
@@ -68,7 +69,6 @@ foreach unit_test_program : unit_test_programs
unit_test = executable(unit_test_name,
sources: [
'unit-tests/test-lib.c',
- 'unit-tests/lib-oid.c',
'unit-tests/lib-reftable.c',
unit_test_program,
],
@@ -500,6 +500,7 @@ integration_tests = [
't4067-diff-partial-clone.sh',
't4068-diff-symmetric-merge-base.sh',
't4069-remerge-diff.sh',
+ 't4070-diff-pairs.sh',
't4100-apply-stat.sh',
't4101-apply-nonl.sh',
't4102-apply-rename.sh',
@@ -729,6 +730,7 @@ integration_tests = [
't5703-upload-pack-ref-in-want.sh',
't5704-protocol-violations.sh',
't5705-session-id-in-capabilities.sh',
+ 't5710-promisor-remote-capability.sh',
't5730-protocol-v2-bundle-uri-file.sh',
't5731-protocol-v2-bundle-uri-git.sh',
't5732-protocol-v2-bundle-uri-http.sh',
diff --git a/t/t0091-bugreport.sh b/t/t0091-bugreport.sh
index e11d819b62..e38ca7a901 100755
--- a/t/t0091-bugreport.sh
+++ b/t/t0091-bugreport.sh
@@ -47,7 +47,8 @@ test_expect_success 'sanity check "System Info" section' '
# This is bound to differ from environment to environment,
# so we just do some rather high-level checks.
grep "uname: ." system &&
- grep "compiler info: ." system
+ grep "compiler info: ." system &&
+ grep "zlib." system
'
test_expect_success 'dies if file with same name as report already exists' '
diff --git a/t/t0450-txt-doc-vs-help.sh b/t/t0450-txt-doc-vs-help.sh
index 853101b86e..2f7504ae7e 100755
--- a/t/t0450-txt-doc-vs-help.sh
+++ b/t/t0450-txt-doc-vs-help.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-test_description='assert (unbuilt) Documentation/*.txt and -h output
+test_description='assert (unbuilt) Documentation/*.adoc and -h output
Run this with --debug to see a summary of where we still fail to make
the two versions consistent with one another.'
@@ -11,11 +11,11 @@ test_expect_success 'setup: list of builtins' '
git --list-cmds=builtins >builtins
'
-test_expect_success 'list of txt and help mismatches is sorted' '
- sort -u "$TEST_DIRECTORY"/t0450/txt-help-mismatches >expect &&
- if ! test_cmp expect "$TEST_DIRECTORY"/t0450/txt-help-mismatches
+test_expect_success 'list of adoc and help mismatches is sorted' '
+ sort -u "$TEST_DIRECTORY"/t0450/adoc-help-mismatches >expect &&
+ if ! test_cmp expect "$TEST_DIRECTORY"/t0450/adoc-help-mismatches
then
- BUG "please keep the list of txt and help mismatches sorted"
+ BUG "please keep the list of adoc and help mismatches sorted"
fi
'
@@ -40,20 +40,20 @@ help_to_synopsis () {
echo "$out"
}
-builtin_to_txt () {
- echo "$GIT_BUILD_DIR/Documentation/git-$1.txt"
+builtin_to_adoc () {
+ echo "$GIT_BUILD_DIR/Documentation/git-$1.adoc"
}
-txt_to_synopsis () {
+adoc_to_synopsis () {
builtin="$1" &&
out_dir="out/$builtin" &&
- out="$out_dir/txt.synopsis" &&
+ out="$out_dir/adoc.synopsis" &&
if test -f "$out"
then
echo "$out" &&
return 0
fi &&
- b2t="$(builtin_to_txt "$builtin")" &&
+ b2t="$(builtin_to_adoc "$builtin")" &&
sed -n \
-E '/^\[(verse|synopsis)\]$/,/^$/ {
/^$/d;
@@ -109,29 +109,29 @@ do
fi
'
- txt="$(builtin_to_txt "$builtin")" &&
- preq="$(echo BUILTIN_TXT_$builtin | tr '[:lower:]-' '[:upper:]_')" &&
+ adoc="$(builtin_to_adoc "$builtin")" &&
+ preq="$(echo BUILTIN_ADOC_$builtin | tr '[:lower:]-' '[:upper:]_')" &&
- if test -f "$txt"
+ if test -f "$adoc"
then
test_set_prereq "$preq"
fi &&
- # *.txt output assertions
- test_expect_success "$preq" "$builtin *.txt SYNOPSIS has dashed labels" '
- check_dashed_labels "$(txt_to_synopsis "$builtin")"
+ # *.adoc output assertions
+ test_expect_success "$preq" "$builtin *.adoc SYNOPSIS has dashed labels" '
+ check_dashed_labels "$(adoc_to_synopsis "$builtin")"
'
- # *.txt output consistency assertions
+ # *.adoc output consistency assertions
result=
- if grep -q "^$builtin$" "$TEST_DIRECTORY"/t0450/txt-help-mismatches
+ if grep -q "^$builtin$" "$TEST_DIRECTORY"/t0450/adoc-help-mismatches
then
result=failure
else
result=success
fi &&
test_expect_$result "$preq" "$builtin -h output and SYNOPSIS agree" '
- t2s="$(txt_to_synopsis "$builtin")" &&
+ t2s="$(adoc_to_synopsis "$builtin")" &&
if test "$builtin" = "merge-tree"
then
test_when_finished "rm -f t2s.new" &&
@@ -140,17 +140,17 @@ do
fi &&
h2s="$(help_to_synopsis "$builtin")" &&
- # The *.txt and -h use different spacing for the
+ # The *.adoc and -h use different spacing for the
# alignment of continued usage output, normalize it.
- align_after_nl "$builtin" <"$t2s" >txt &&
+ align_after_nl "$builtin" <"$t2s" >adoc &&
align_after_nl "$builtin" <"$h2s" >help &&
- test_cmp txt help
+ test_cmp adoc help
'
- if test_have_prereq "$preq" && test -e txt && test -e help
+ if test_have_prereq "$preq" && test -e adoc && test -e help
then
test_debug '
- if test_cmp txt help >cmp 2>/dev/null
+ if test_cmp adoc help >cmp 2>/dev/null
then
echo "=== DONE: $builtin ==="
else
@@ -161,7 +161,7 @@ do
# Not in test_expect_success in case --run is being
# used with --debug
- rm -f txt help tmp 2>/dev/null
+ rm -f adoc help tmp 2>/dev/null
fi
done <builtins
diff --git a/t/t0450/txt-help-mismatches b/t/t0450/adoc-help-mismatches
index c4a15fd0cb..c4a15fd0cb 100644
--- a/t/t0450/txt-help-mismatches
+++ b/t/t0450/adoc-help-mismatches
diff --git a/t/t0602-reffiles-fsck.sh b/t/t0602-reffiles-fsck.sh
index d4a08b823b..9d1dc2144c 100755
--- a/t/t0602-reffiles-fsck.sh
+++ b/t/t0602-reffiles-fsck.sh
@@ -14,222 +14,229 @@ test_expect_success 'ref name should be checked' '
git init repo &&
branch_dir_prefix=.git/refs/heads &&
tag_dir_prefix=.git/refs/tags &&
- cd repo &&
-
- git commit --allow-empty -m initial &&
- git checkout -b default-branch &&
- git tag default-tag &&
- git tag multi_hierarchy/default-tag &&
-
- cp $branch_dir_prefix/default-branch $branch_dir_prefix/@ &&
- git refs verify 2>err &&
- test_must_be_empty err &&
- rm $branch_dir_prefix/@ &&
-
- cp $tag_dir_prefix/default-tag $tag_dir_prefix/tag-1.lock &&
- git refs verify 2>err &&
- rm $tag_dir_prefix/tag-1.lock &&
- test_must_be_empty err &&
-
- cp $tag_dir_prefix/default-tag $tag_dir_prefix/.lock &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/tags/.lock: badRefName: invalid refname format
- EOF
- rm $tag_dir_prefix/.lock &&
- test_cmp expect err &&
-
- for refname in ".refname-starts-with-dot" "~refname-has-stride"
- do
- cp $branch_dir_prefix/default-branch "$branch_dir_prefix/$refname" &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/heads/$refname: badRefName: invalid refname format
- EOF
- rm "$branch_dir_prefix/$refname" &&
- test_cmp expect err || return 1
- done &&
+ (
+ cd repo &&
- for refname in ".refname-starts-with-dot" "~refname-has-stride"
- do
- cp $tag_dir_prefix/default-tag "$tag_dir_prefix/$refname" &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/tags/$refname: badRefName: invalid refname format
- EOF
- rm "$tag_dir_prefix/$refname" &&
- test_cmp expect err || return 1
- done &&
+ git commit --allow-empty -m initial &&
+ git checkout -b default-branch &&
+ git tag default-tag &&
+ git tag multi_hierarchy/default-tag &&
- for refname in ".refname-starts-with-dot" "~refname-has-stride"
- do
- cp $tag_dir_prefix/multi_hierarchy/default-tag "$tag_dir_prefix/multi_hierarchy/$refname" &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/tags/multi_hierarchy/$refname: badRefName: invalid refname format
- EOF
- rm "$tag_dir_prefix/multi_hierarchy/$refname" &&
- test_cmp expect err || return 1
- done &&
+ cp $branch_dir_prefix/default-branch $branch_dir_prefix/@ &&
+ git refs verify 2>err &&
+ test_must_be_empty err &&
+ rm $branch_dir_prefix/@ &&
+
+ cp $tag_dir_prefix/default-tag $tag_dir_prefix/tag-1.lock &&
+ git refs verify 2>err &&
+ rm $tag_dir_prefix/tag-1.lock &&
+ test_must_be_empty err &&
- for refname in ".refname-starts-with-dot" "~refname-has-stride"
- do
- mkdir "$branch_dir_prefix/$refname" &&
- cp $branch_dir_prefix/default-branch "$branch_dir_prefix/$refname/default-branch" &&
+ cp $tag_dir_prefix/default-tag $tag_dir_prefix/.lock &&
test_must_fail git refs verify 2>err &&
cat >expect <<-EOF &&
- error: refs/heads/$refname/default-branch: badRefName: invalid refname format
+ error: refs/tags/.lock: badRefName: invalid refname format
EOF
- rm -r "$branch_dir_prefix/$refname" &&
- test_cmp expect err || return 1
- done
+ rm $tag_dir_prefix/.lock &&
+ test_cmp expect err &&
+
+ for refname in ".refname-starts-with-dot" "~refname-has-stride"
+ do
+ cp $branch_dir_prefix/default-branch "$branch_dir_prefix/$refname" &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/$refname: badRefName: invalid refname format
+ EOF
+ rm "$branch_dir_prefix/$refname" &&
+ test_cmp expect err || return 1
+ done &&
+
+ for refname in ".refname-starts-with-dot" "~refname-has-stride"
+ do
+ cp $tag_dir_prefix/default-tag "$tag_dir_prefix/$refname" &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/tags/$refname: badRefName: invalid refname format
+ EOF
+ rm "$tag_dir_prefix/$refname" &&
+ test_cmp expect err || return 1
+ done &&
+
+ for refname in ".refname-starts-with-dot" "~refname-has-stride"
+ do
+ cp $tag_dir_prefix/multi_hierarchy/default-tag "$tag_dir_prefix/multi_hierarchy/$refname" &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/tags/multi_hierarchy/$refname: badRefName: invalid refname format
+ EOF
+ rm "$tag_dir_prefix/multi_hierarchy/$refname" &&
+ test_cmp expect err || return 1
+ done &&
+
+ for refname in ".refname-starts-with-dot" "~refname-has-stride"
+ do
+ mkdir "$branch_dir_prefix/$refname" &&
+ cp $branch_dir_prefix/default-branch "$branch_dir_prefix/$refname/default-branch" &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/$refname/default-branch: badRefName: invalid refname format
+ EOF
+ rm -r "$branch_dir_prefix/$refname" &&
+ test_cmp expect err || return 1
+ done
+ )
'
test_expect_success 'ref name check should be adapted into fsck messages' '
test_when_finished "rm -rf repo" &&
git init repo &&
branch_dir_prefix=.git/refs/heads &&
- cd repo &&
- git commit --allow-empty -m initial &&
- git checkout -b branch-1 &&
-
- cp $branch_dir_prefix/branch-1 $branch_dir_prefix/.branch-1 &&
- git -c fsck.badRefName=warn refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/.branch-1: badRefName: invalid refname format
- EOF
- rm $branch_dir_prefix/.branch-1 &&
- test_cmp expect err &&
-
- cp $branch_dir_prefix/branch-1 $branch_dir_prefix/.branch-1 &&
- git -c fsck.badRefName=ignore refs verify 2>err &&
- test_must_be_empty err
+ (
+ cd repo &&
+ git commit --allow-empty -m initial &&
+ git checkout -b branch-1 &&
+
+ cp $branch_dir_prefix/branch-1 $branch_dir_prefix/.branch-1 &&
+ git -c fsck.badRefName=warn refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/.branch-1: badRefName: invalid refname format
+ EOF
+ rm $branch_dir_prefix/.branch-1 &&
+ test_cmp expect err &&
+
+ cp $branch_dir_prefix/branch-1 $branch_dir_prefix/.branch-1 &&
+ git -c fsck.badRefName=ignore refs verify 2>err &&
+ test_must_be_empty err
+ )
'
test_expect_success 'ref name check should work for multiple worktrees' '
test_when_finished "rm -rf repo" &&
git init repo &&
-
- cd repo &&
- test_commit initial &&
- git checkout -b branch-1 &&
- test_commit second &&
- git checkout -b branch-2 &&
- test_commit third &&
- git checkout -b branch-3 &&
- git worktree add ./worktree-1 branch-1 &&
- git worktree add ./worktree-2 branch-2 &&
- worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
- worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
-
(
- cd worktree-1 &&
- git update-ref refs/worktree/branch-4 refs/heads/branch-3
- ) &&
- (
- cd worktree-2 &&
- git update-ref refs/worktree/branch-4 refs/heads/branch-3
- ) &&
-
- cp $worktree1_refdir_prefix/branch-4 $worktree1_refdir_prefix/'\'' branch-5'\'' &&
- cp $worktree2_refdir_prefix/branch-4 $worktree2_refdir_prefix/'\''~branch-6'\'' &&
-
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: worktrees/worktree-1/refs/worktree/ branch-5: badRefName: invalid refname format
- error: worktrees/worktree-2/refs/worktree/~branch-6: badRefName: invalid refname format
- EOF
- sort err >sorted_err &&
- test_cmp expect sorted_err &&
-
- for worktree in "worktree-1" "worktree-2"
- do
+ cd repo &&
+ test_commit initial &&
+ git checkout -b branch-1 &&
+ test_commit second &&
+ git checkout -b branch-2 &&
+ test_commit third &&
+ git checkout -b branch-3 &&
+ git worktree add ./worktree-1 branch-1 &&
+ git worktree add ./worktree-2 branch-2 &&
+ worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
+ worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
+
(
- cd $worktree &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: worktrees/worktree-1/refs/worktree/ branch-5: badRefName: invalid refname format
- error: worktrees/worktree-2/refs/worktree/~branch-6: badRefName: invalid refname format
- EOF
- sort err >sorted_err &&
- test_cmp expect sorted_err || return 1
- )
- done
+ cd worktree-1 &&
+ git update-ref refs/worktree/branch-4 refs/heads/branch-3
+ ) &&
+ (
+ cd worktree-2 &&
+ git update-ref refs/worktree/branch-4 refs/heads/branch-3
+ ) &&
+
+ cp $worktree1_refdir_prefix/branch-4 $worktree1_refdir_prefix/'\'' branch-5'\'' &&
+ cp $worktree2_refdir_prefix/branch-4 $worktree2_refdir_prefix/'\''~branch-6'\'' &&
+
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: worktrees/worktree-1/refs/worktree/ branch-5: badRefName: invalid refname format
+ error: worktrees/worktree-2/refs/worktree/~branch-6: badRefName: invalid refname format
+ EOF
+ sort err >sorted_err &&
+ test_cmp expect sorted_err &&
+
+ for worktree in "worktree-1" "worktree-2"
+ do
+ (
+ cd $worktree &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: worktrees/worktree-1/refs/worktree/ branch-5: badRefName: invalid refname format
+ error: worktrees/worktree-2/refs/worktree/~branch-6: badRefName: invalid refname format
+ EOF
+ sort err >sorted_err &&
+ test_cmp expect sorted_err || return 1
+ )
+ done
+ )
'
test_expect_success 'regular ref content should be checked (individual)' '
test_when_finished "rm -rf repo" &&
git init repo &&
branch_dir_prefix=.git/refs/heads &&
- cd repo &&
- test_commit default &&
- mkdir -p "$branch_dir_prefix/a/b" &&
+ (
+ cd repo &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
- git refs verify 2>err &&
- test_must_be_empty err &&
+ git refs verify 2>err &&
+ test_must_be_empty err &&
- for bad_content in "$(git rev-parse main)x" "xfsazqfxcadas" "Xfsazqfxcadas"
- do
- printf "%s" $bad_content >$branch_dir_prefix/branch-bad &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/heads/branch-bad: badRefContent: $bad_content
- EOF
- rm $branch_dir_prefix/branch-bad &&
- test_cmp expect err || return 1
- done &&
+ for bad_content in "$(git rev-parse main)x" "xfsazqfxcadas" "Xfsazqfxcadas"
+ do
+ printf "%s" $bad_content >$branch_dir_prefix/branch-bad &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/branch-bad: badRefContent: $bad_content
+ EOF
+ rm $branch_dir_prefix/branch-bad &&
+ test_cmp expect err || return 1
+ done &&
- for bad_content in "$(git rev-parse main)x" "xfsazqfxcadas" "Xfsazqfxcadas"
- do
- printf "%s" $bad_content >$branch_dir_prefix/a/b/branch-bad &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/heads/a/b/branch-bad: badRefContent: $bad_content
- EOF
- rm $branch_dir_prefix/a/b/branch-bad &&
- test_cmp expect err || return 1
- done &&
-
- printf "%s" "$(git rev-parse main)" >$branch_dir_prefix/branch-no-newline &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-no-newline: refMissingNewline: misses LF at the end
- EOF
- rm $branch_dir_prefix/branch-no-newline &&
- test_cmp expect err &&
-
- for trailing_content in " garbage" " more garbage"
- do
- printf "%s" "$(git rev-parse main)$trailing_content" >$branch_dir_prefix/branch-garbage &&
+ for bad_content in "$(git rev-parse main)x" "xfsazqfxcadas" "Xfsazqfxcadas"
+ do
+ printf "%s" $bad_content >$branch_dir_prefix/a/b/branch-bad &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/a/b/branch-bad: badRefContent: $bad_content
+ EOF
+ rm $branch_dir_prefix/a/b/branch-bad &&
+ test_cmp expect err || return 1
+ done &&
+
+ printf "%s" "$(git rev-parse main)" >$branch_dir_prefix/branch-no-newline &&
git refs verify 2>err &&
cat >expect <<-EOF &&
- warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\''$trailing_content'\''
+ warning: refs/heads/branch-no-newline: refMissingNewline: misses LF at the end
EOF
- rm $branch_dir_prefix/branch-garbage &&
- test_cmp expect err || return 1
- done &&
+ rm $branch_dir_prefix/branch-no-newline &&
+ test_cmp expect err &&
- printf "%s\n\n\n" "$(git rev-parse main)" >$branch_dir_prefix/branch-garbage-special &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-garbage-special: trailingRefContent: has trailing garbage: '\''
+ for trailing_content in " garbage" " more garbage"
+ do
+ printf "%s" "$(git rev-parse main)$trailing_content" >$branch_dir_prefix/branch-garbage &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\''$trailing_content'\''
+ EOF
+ rm $branch_dir_prefix/branch-garbage &&
+ test_cmp expect err || return 1
+ done &&
+ printf "%s\n\n\n" "$(git rev-parse main)" >$branch_dir_prefix/branch-garbage-special &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-garbage-special: trailingRefContent: has trailing garbage: '\''
- '\''
- EOF
- rm $branch_dir_prefix/branch-garbage-special &&
- test_cmp expect err &&
- printf "%s\n\n\n garbage" "$(git rev-parse main)" >$branch_dir_prefix/branch-garbage-special &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-garbage-special: trailingRefContent: has trailing garbage: '\''
+ '\''
+ EOF
+ rm $branch_dir_prefix/branch-garbage-special &&
+ test_cmp expect err &&
+
+ printf "%s\n\n\n garbage" "$(git rev-parse main)" >$branch_dir_prefix/branch-garbage-special &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-garbage-special: trailingRefContent: has trailing garbage: '\''
- garbage'\''
- EOF
- rm $branch_dir_prefix/branch-garbage-special &&
- test_cmp expect err
+ garbage'\''
+ EOF
+ rm $branch_dir_prefix/branch-garbage-special &&
+ test_cmp expect err
+ )
'
test_expect_success 'regular ref content should be checked (aggregate)' '
@@ -237,99 +244,103 @@ test_expect_success 'regular ref content should be checked (aggregate)' '
git init repo &&
branch_dir_prefix=.git/refs/heads &&
tag_dir_prefix=.git/refs/tags &&
- cd repo &&
- test_commit default &&
- mkdir -p "$branch_dir_prefix/a/b" &&
-
- bad_content_1=$(git rev-parse main)x &&
- bad_content_2=xfsazqfxcadas &&
- bad_content_3=Xfsazqfxcadas &&
- printf "%s" $bad_content_1 >$tag_dir_prefix/tag-bad-1 &&
- printf "%s" $bad_content_2 >$tag_dir_prefix/tag-bad-2 &&
- printf "%s" $bad_content_3 >$branch_dir_prefix/a/b/branch-bad &&
- printf "%s" "$(git rev-parse main)" >$branch_dir_prefix/branch-no-newline &&
- printf "%s garbage" "$(git rev-parse main)" >$branch_dir_prefix/branch-garbage &&
-
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/heads/a/b/branch-bad: badRefContent: $bad_content_3
- error: refs/tags/tag-bad-1: badRefContent: $bad_content_1
- error: refs/tags/tag-bad-2: badRefContent: $bad_content_2
- warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\'' garbage'\''
- warning: refs/heads/branch-no-newline: refMissingNewline: misses LF at the end
- EOF
- sort err >sorted_err &&
- test_cmp expect sorted_err
+ (
+ cd repo &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
+
+ bad_content_1=$(git rev-parse main)x &&
+ bad_content_2=xfsazqfxcadas &&
+ bad_content_3=Xfsazqfxcadas &&
+ printf "%s" $bad_content_1 >$tag_dir_prefix/tag-bad-1 &&
+ printf "%s" $bad_content_2 >$tag_dir_prefix/tag-bad-2 &&
+ printf "%s" $bad_content_3 >$branch_dir_prefix/a/b/branch-bad &&
+ printf "%s" "$(git rev-parse main)" >$branch_dir_prefix/branch-no-newline &&
+ printf "%s garbage" "$(git rev-parse main)" >$branch_dir_prefix/branch-garbage &&
+
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/a/b/branch-bad: badRefContent: $bad_content_3
+ error: refs/tags/tag-bad-1: badRefContent: $bad_content_1
+ error: refs/tags/tag-bad-2: badRefContent: $bad_content_2
+ warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\'' garbage'\''
+ warning: refs/heads/branch-no-newline: refMissingNewline: misses LF at the end
+ EOF
+ sort err >sorted_err &&
+ test_cmp expect sorted_err
+ )
'
test_expect_success 'textual symref content should be checked (individual)' '
test_when_finished "rm -rf repo" &&
git init repo &&
branch_dir_prefix=.git/refs/heads &&
- cd repo &&
- test_commit default &&
- mkdir -p "$branch_dir_prefix/a/b" &&
+ (
+ cd repo &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
+
+ for good_referent in "refs/heads/branch" "HEAD"
+ do
+ printf "ref: %s\n" $good_referent >$branch_dir_prefix/branch-good &&
+ git refs verify 2>err &&
+ rm $branch_dir_prefix/branch-good &&
+ test_must_be_empty err || return 1
+ done &&
+
+ for bad_referent in "refs/heads/.branch" "refs/heads/~branch" "refs/heads/?branch"
+ do
+ printf "ref: %s\n" $bad_referent >$branch_dir_prefix/branch-bad &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/branch-bad: badReferentName: points to invalid refname '\''$bad_referent'\''
+ EOF
+ rm $branch_dir_prefix/branch-bad &&
+ test_cmp expect err || return 1
+ done &&
- for good_referent in "refs/heads/branch" "HEAD"
- do
- printf "ref: %s\n" $good_referent >$branch_dir_prefix/branch-good &&
+ printf "ref: refs/heads/branch" >$branch_dir_prefix/branch-no-newline &&
git refs verify 2>err &&
- rm $branch_dir_prefix/branch-good &&
- test_must_be_empty err || return 1
- done &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-no-newline: refMissingNewline: misses LF at the end
+ EOF
+ rm $branch_dir_prefix/branch-no-newline &&
+ test_cmp expect err &&
- for bad_referent in "refs/heads/.branch" "refs/heads/~branch" "refs/heads/?branch"
- do
- printf "ref: %s\n" $bad_referent >$branch_dir_prefix/branch-bad &&
- test_must_fail git refs verify 2>err &&
+ printf "ref: refs/heads/branch " >$branch_dir_prefix/a/b/branch-trailing-1 &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/a/b/branch-trailing-1: refMissingNewline: misses LF at the end
+ warning: refs/heads/a/b/branch-trailing-1: trailingRefContent: has trailing whitespaces or newlines
+ EOF
+ rm $branch_dir_prefix/a/b/branch-trailing-1 &&
+ test_cmp expect err &&
+
+ printf "ref: refs/heads/branch\n\n" >$branch_dir_prefix/a/b/branch-trailing-2 &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/a/b/branch-trailing-2: trailingRefContent: has trailing whitespaces or newlines
+ EOF
+ rm $branch_dir_prefix/a/b/branch-trailing-2 &&
+ test_cmp expect err &&
+
+ printf "ref: refs/heads/branch \n" >$branch_dir_prefix/a/b/branch-trailing-3 &&
+ git refs verify 2>err &&
cat >expect <<-EOF &&
- error: refs/heads/branch-bad: badReferentName: points to invalid refname '\''$bad_referent'\''
- EOF
- rm $branch_dir_prefix/branch-bad &&
- test_cmp expect err || return 1
- done &&
-
- printf "ref: refs/heads/branch" >$branch_dir_prefix/branch-no-newline &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-no-newline: refMissingNewline: misses LF at the end
- EOF
- rm $branch_dir_prefix/branch-no-newline &&
- test_cmp expect err &&
-
- printf "ref: refs/heads/branch " >$branch_dir_prefix/a/b/branch-trailing-1 &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/a/b/branch-trailing-1: refMissingNewline: misses LF at the end
- warning: refs/heads/a/b/branch-trailing-1: trailingRefContent: has trailing whitespaces or newlines
- EOF
- rm $branch_dir_prefix/a/b/branch-trailing-1 &&
- test_cmp expect err &&
-
- printf "ref: refs/heads/branch\n\n" >$branch_dir_prefix/a/b/branch-trailing-2 &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/a/b/branch-trailing-2: trailingRefContent: has trailing whitespaces or newlines
- EOF
- rm $branch_dir_prefix/a/b/branch-trailing-2 &&
- test_cmp expect err &&
-
- printf "ref: refs/heads/branch \n" >$branch_dir_prefix/a/b/branch-trailing-3 &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/a/b/branch-trailing-3: trailingRefContent: has trailing whitespaces or newlines
- EOF
- rm $branch_dir_prefix/a/b/branch-trailing-3 &&
- test_cmp expect err &&
-
- printf "ref: refs/heads/branch \n " >$branch_dir_prefix/a/b/branch-complicated &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/a/b/branch-complicated: refMissingNewline: misses LF at the end
- warning: refs/heads/a/b/branch-complicated: trailingRefContent: has trailing whitespaces or newlines
- EOF
- rm $branch_dir_prefix/a/b/branch-complicated &&
- test_cmp expect err
+ warning: refs/heads/a/b/branch-trailing-3: trailingRefContent: has trailing whitespaces or newlines
+ EOF
+ rm $branch_dir_prefix/a/b/branch-trailing-3 &&
+ test_cmp expect err &&
+
+ printf "ref: refs/heads/branch \n " >$branch_dir_prefix/a/b/branch-complicated &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/a/b/branch-complicated: refMissingNewline: misses LF at the end
+ warning: refs/heads/a/b/branch-complicated: trailingRefContent: has trailing whitespaces or newlines
+ EOF
+ rm $branch_dir_prefix/a/b/branch-complicated &&
+ test_cmp expect err
+ )
'
test_expect_success 'textual symref content should be checked (aggregate)' '
@@ -337,32 +348,34 @@ test_expect_success 'textual symref content should be checked (aggregate)' '
git init repo &&
branch_dir_prefix=.git/refs/heads &&
tag_dir_prefix=.git/refs/tags &&
- cd repo &&
- test_commit default &&
- mkdir -p "$branch_dir_prefix/a/b" &&
-
- printf "ref: refs/heads/branch\n" >$branch_dir_prefix/branch-good &&
- printf "ref: HEAD\n" >$branch_dir_prefix/branch-head &&
- printf "ref: refs/heads/branch" >$branch_dir_prefix/branch-no-newline-1 &&
- printf "ref: refs/heads/branch " >$branch_dir_prefix/a/b/branch-trailing-1 &&
- printf "ref: refs/heads/branch\n\n" >$branch_dir_prefix/a/b/branch-trailing-2 &&
- printf "ref: refs/heads/branch \n" >$branch_dir_prefix/a/b/branch-trailing-3 &&
- printf "ref: refs/heads/branch \n " >$branch_dir_prefix/a/b/branch-complicated &&
- printf "ref: refs/heads/.branch\n" >$branch_dir_prefix/branch-bad-1 &&
-
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- error: refs/heads/branch-bad-1: badReferentName: points to invalid refname '\''refs/heads/.branch'\''
- warning: refs/heads/a/b/branch-complicated: refMissingNewline: misses LF at the end
- warning: refs/heads/a/b/branch-complicated: trailingRefContent: has trailing whitespaces or newlines
- warning: refs/heads/a/b/branch-trailing-1: refMissingNewline: misses LF at the end
- warning: refs/heads/a/b/branch-trailing-1: trailingRefContent: has trailing whitespaces or newlines
- warning: refs/heads/a/b/branch-trailing-2: trailingRefContent: has trailing whitespaces or newlines
- warning: refs/heads/a/b/branch-trailing-3: trailingRefContent: has trailing whitespaces or newlines
- warning: refs/heads/branch-no-newline-1: refMissingNewline: misses LF at the end
- EOF
- sort err >sorted_err &&
- test_cmp expect sorted_err
+ (
+ cd repo &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
+
+ printf "ref: refs/heads/branch\n" >$branch_dir_prefix/branch-good &&
+ printf "ref: HEAD\n" >$branch_dir_prefix/branch-head &&
+ printf "ref: refs/heads/branch" >$branch_dir_prefix/branch-no-newline-1 &&
+ printf "ref: refs/heads/branch " >$branch_dir_prefix/a/b/branch-trailing-1 &&
+ printf "ref: refs/heads/branch\n\n" >$branch_dir_prefix/a/b/branch-trailing-2 &&
+ printf "ref: refs/heads/branch \n" >$branch_dir_prefix/a/b/branch-trailing-3 &&
+ printf "ref: refs/heads/branch \n " >$branch_dir_prefix/a/b/branch-complicated &&
+ printf "ref: refs/heads/.branch\n" >$branch_dir_prefix/branch-bad-1 &&
+
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: refs/heads/branch-bad-1: badReferentName: points to invalid refname '\''refs/heads/.branch'\''
+ warning: refs/heads/a/b/branch-complicated: refMissingNewline: misses LF at the end
+ warning: refs/heads/a/b/branch-complicated: trailingRefContent: has trailing whitespaces or newlines
+ warning: refs/heads/a/b/branch-trailing-1: refMissingNewline: misses LF at the end
+ warning: refs/heads/a/b/branch-trailing-1: trailingRefContent: has trailing whitespaces or newlines
+ warning: refs/heads/a/b/branch-trailing-2: trailingRefContent: has trailing whitespaces or newlines
+ warning: refs/heads/a/b/branch-trailing-3: trailingRefContent: has trailing whitespaces or newlines
+ warning: refs/heads/branch-no-newline-1: refMissingNewline: misses LF at the end
+ EOF
+ sort err >sorted_err &&
+ test_cmp expect sorted_err
+ )
'
test_expect_success 'the target of the textual symref should be checked' '
@@ -370,230 +383,490 @@ test_expect_success 'the target of the textual symref should be checked' '
git init repo &&
branch_dir_prefix=.git/refs/heads &&
tag_dir_prefix=.git/refs/tags &&
- cd repo &&
- test_commit default &&
- mkdir -p "$branch_dir_prefix/a/b" &&
+ (
+ cd repo &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
+
+ for good_referent in "refs/heads/branch" "HEAD" "refs/tags/tag"
+ do
+ printf "ref: %s\n" $good_referent >$branch_dir_prefix/branch-good &&
+ git refs verify 2>err &&
+ rm $branch_dir_prefix/branch-good &&
+ test_must_be_empty err || return 1
+ done &&
+
+ for nonref_referent in "refs-back/heads/branch" "refs-back/tags/tag" "reflogs/refs/heads/branch"
+ do
+ printf "ref: %s\n" $nonref_referent >$branch_dir_prefix/branch-bad-1 &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-bad-1: symrefTargetIsNotARef: points to non-ref target '\''$nonref_referent'\''
+ EOF
+ rm $branch_dir_prefix/branch-bad-1 &&
+ test_cmp expect err || return 1
+ done
+ )
+'
+
+test_expect_success SYMLINKS 'symlink symref content should be checked' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ branch_dir_prefix=.git/refs/heads &&
+ tag_dir_prefix=.git/refs/tags &&
+ (
+ cd repo &&
+ test_commit default &&
+ mkdir -p "$branch_dir_prefix/a/b" &&
- for good_referent in "refs/heads/branch" "HEAD" "refs/tags/tag"
- do
- printf "ref: %s\n" $good_referent >$branch_dir_prefix/branch-good &&
+ ln -sf ./main $branch_dir_prefix/branch-symbolic-good &&
git refs verify 2>err &&
- rm $branch_dir_prefix/branch-good &&
- test_must_be_empty err || return 1
- done &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
+ EOF
+ rm $branch_dir_prefix/branch-symbolic-good &&
+ test_cmp expect err &&
- for nonref_referent in "refs-back/heads/branch" "refs-back/tags/tag" "reflogs/refs/heads/branch"
- do
- printf "ref: %s\n" $nonref_referent >$branch_dir_prefix/branch-bad-1 &&
+ ln -sf ../../logs/branch-escape $branch_dir_prefix/branch-symbolic &&
git refs verify 2>err &&
cat >expect <<-EOF &&
- warning: refs/heads/branch-bad-1: symrefTargetIsNotARef: points to non-ref target '\''$nonref_referent'\''
+ warning: refs/heads/branch-symbolic: symlinkRef: use deprecated symbolic link for symref
+ warning: refs/heads/branch-symbolic: symrefTargetIsNotARef: points to non-ref target '\''logs/branch-escape'\''
+ EOF
+ rm $branch_dir_prefix/branch-symbolic &&
+ test_cmp expect err &&
+
+ ln -sf ./"branch " $branch_dir_prefix/branch-symbolic-bad &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-symbolic-bad: symlinkRef: use deprecated symbolic link for symref
+ error: refs/heads/branch-symbolic-bad: badReferentName: points to invalid refname '\''refs/heads/branch '\''
+ EOF
+ rm $branch_dir_prefix/branch-symbolic-bad &&
+ test_cmp expect err &&
+
+ ln -sf ./".tag" $tag_dir_prefix/tag-symbolic-1 &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/tags/tag-symbolic-1: symlinkRef: use deprecated symbolic link for symref
+ error: refs/tags/tag-symbolic-1: badReferentName: points to invalid refname '\''refs/tags/.tag'\''
EOF
- rm $branch_dir_prefix/branch-bad-1 &&
- test_cmp expect err || return 1
- done
+ rm $tag_dir_prefix/tag-symbolic-1 &&
+ test_cmp expect err
+ )
'
-test_expect_success SYMLINKS 'symlink symref content should be checked' '
+test_expect_success SYMLINKS 'symlink symref content should be checked (worktree)' '
test_when_finished "rm -rf repo" &&
git init repo &&
- branch_dir_prefix=.git/refs/heads &&
- tag_dir_prefix=.git/refs/tags &&
- cd repo &&
- test_commit default &&
- mkdir -p "$branch_dir_prefix/a/b" &&
-
- ln -sf ./main $branch_dir_prefix/branch-symbolic-good &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
- EOF
- rm $branch_dir_prefix/branch-symbolic-good &&
- test_cmp expect err &&
-
- ln -sf ../../logs/branch-escape $branch_dir_prefix/branch-symbolic &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-symbolic: symlinkRef: use deprecated symbolic link for symref
- warning: refs/heads/branch-symbolic: symrefTargetIsNotARef: points to non-ref target '\''logs/branch-escape'\''
- EOF
- rm $branch_dir_prefix/branch-symbolic &&
- test_cmp expect err &&
-
- ln -sf ./"branch " $branch_dir_prefix/branch-symbolic-bad &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-symbolic-bad: symlinkRef: use deprecated symbolic link for symref
- error: refs/heads/branch-symbolic-bad: badReferentName: points to invalid refname '\''refs/heads/branch '\''
- EOF
- rm $branch_dir_prefix/branch-symbolic-bad &&
- test_cmp expect err &&
-
- ln -sf ./".tag" $tag_dir_prefix/tag-symbolic-1 &&
- test_must_fail git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/tags/tag-symbolic-1: symlinkRef: use deprecated symbolic link for symref
- error: refs/tags/tag-symbolic-1: badReferentName: points to invalid refname '\''refs/tags/.tag'\''
- EOF
- rm $tag_dir_prefix/tag-symbolic-1 &&
- test_cmp expect err
+ (
+ cd repo &&
+ test_commit default &&
+ git branch branch-1 &&
+ git branch branch-2 &&
+ git branch branch-3 &&
+ git worktree add ./worktree-1 branch-2 &&
+ git worktree add ./worktree-2 branch-3 &&
+ main_worktree_refdir_prefix=.git/refs/heads &&
+ worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
+ worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
+
+ (
+ cd worktree-1 &&
+ git update-ref refs/worktree/branch-4 refs/heads/branch-1
+ ) &&
+ (
+ cd worktree-2 &&
+ git update-ref refs/worktree/branch-4 refs/heads/branch-1
+ ) &&
+
+ ln -sf ../../../../refs/heads/good-branch $worktree1_refdir_prefix/branch-symbolic-good &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-1/refs/worktree/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
+ EOF
+ rm $worktree1_refdir_prefix/branch-symbolic-good &&
+ test_cmp expect err &&
+
+ ln -sf ../../../../worktrees/worktree-1/good-branch $worktree2_refdir_prefix/branch-symbolic-good &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-2/refs/worktree/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
+ EOF
+ rm $worktree2_refdir_prefix/branch-symbolic-good &&
+ test_cmp expect err &&
+
+ ln -sf ../../worktrees/worktree-2/good-branch $main_worktree_refdir_prefix/branch-symbolic-good &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
+ EOF
+ rm $main_worktree_refdir_prefix/branch-symbolic-good &&
+ test_cmp expect err &&
+
+ ln -sf ../../../../logs/branch-escape $worktree1_refdir_prefix/branch-symbolic &&
+ git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-1/refs/worktree/branch-symbolic: symlinkRef: use deprecated symbolic link for symref
+ warning: worktrees/worktree-1/refs/worktree/branch-symbolic: symrefTargetIsNotARef: points to non-ref target '\''logs/branch-escape'\''
+ EOF
+ rm $worktree1_refdir_prefix/branch-symbolic &&
+ test_cmp expect err &&
+
+ for bad_referent_name in ".tag" "branch "
+ do
+ ln -sf ./"$bad_referent_name" $worktree1_refdir_prefix/bad-symbolic &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-1/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
+ error: worktrees/worktree-1/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''worktrees/worktree-1/refs/worktree/$bad_referent_name'\''
+ EOF
+ rm $worktree1_refdir_prefix/bad-symbolic &&
+ test_cmp expect err &&
+
+ ln -sf ../../../../refs/heads/"$bad_referent_name" $worktree1_refdir_prefix/bad-symbolic &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-1/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
+ error: worktrees/worktree-1/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''refs/heads/$bad_referent_name'\''
+ EOF
+ rm $worktree1_refdir_prefix/bad-symbolic &&
+ test_cmp expect err &&
+
+ ln -sf ./"$bad_referent_name" $worktree2_refdir_prefix/bad-symbolic &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-2/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
+ error: worktrees/worktree-2/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''worktrees/worktree-2/refs/worktree/$bad_referent_name'\''
+ EOF
+ rm $worktree2_refdir_prefix/bad-symbolic &&
+ test_cmp expect err &&
+
+ ln -sf ../../../../refs/heads/"$bad_referent_name" $worktree2_refdir_prefix/bad-symbolic &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ warning: worktrees/worktree-2/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
+ error: worktrees/worktree-2/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''refs/heads/$bad_referent_name'\''
+ EOF
+ rm $worktree2_refdir_prefix/bad-symbolic &&
+ test_cmp expect err || return 1
+ done
+ )
'
-test_expect_success SYMLINKS 'symlink symref content should be checked (worktree)' '
+test_expect_success 'ref content checks should work with worktrees' '
test_when_finished "rm -rf repo" &&
git init repo &&
- cd repo &&
- test_commit default &&
- git branch branch-1 &&
- git branch branch-2 &&
- git branch branch-3 &&
- git worktree add ./worktree-1 branch-2 &&
- git worktree add ./worktree-2 branch-3 &&
- main_worktree_refdir_prefix=.git/refs/heads &&
- worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
- worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
-
- (
- cd worktree-1 &&
- git update-ref refs/worktree/branch-4 refs/heads/branch-1
- ) &&
(
- cd worktree-2 &&
- git update-ref refs/worktree/branch-4 refs/heads/branch-1
- ) &&
-
- ln -sf ../../../../refs/heads/good-branch $worktree1_refdir_prefix/branch-symbolic-good &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: worktrees/worktree-1/refs/worktree/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
- EOF
- rm $worktree1_refdir_prefix/branch-symbolic-good &&
- test_cmp expect err &&
-
- ln -sf ../../../../worktrees/worktree-1/good-branch $worktree2_refdir_prefix/branch-symbolic-good &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: worktrees/worktree-2/refs/worktree/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
- EOF
- rm $worktree2_refdir_prefix/branch-symbolic-good &&
- test_cmp expect err &&
-
- ln -sf ../../worktrees/worktree-2/good-branch $main_worktree_refdir_prefix/branch-symbolic-good &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: refs/heads/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
- EOF
- rm $main_worktree_refdir_prefix/branch-symbolic-good &&
- test_cmp expect err &&
-
- ln -sf ../../../../logs/branch-escape $worktree1_refdir_prefix/branch-symbolic &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: worktrees/worktree-1/refs/worktree/branch-symbolic: symlinkRef: use deprecated symbolic link for symref
- warning: worktrees/worktree-1/refs/worktree/branch-symbolic: symrefTargetIsNotARef: points to non-ref target '\''logs/branch-escape'\''
- EOF
- rm $worktree1_refdir_prefix/branch-symbolic &&
- test_cmp expect err &&
-
- for bad_referent_name in ".tag" "branch "
- do
- ln -sf ./"$bad_referent_name" $worktree1_refdir_prefix/bad-symbolic &&
- test_must_fail git refs verify 2>err &&
+ cd repo &&
+ test_commit default &&
+ git branch branch-1 &&
+ git branch branch-2 &&
+ git branch branch-3 &&
+ git worktree add ./worktree-1 branch-2 &&
+ git worktree add ./worktree-2 branch-3 &&
+ worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
+ worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
+
+ (
+ cd worktree-1 &&
+ git update-ref refs/worktree/branch-4 refs/heads/branch-1
+ ) &&
+ (
+ cd worktree-2 &&
+ git update-ref refs/worktree/branch-4 refs/heads/branch-1
+ ) &&
+
+ for bad_content in "$(git rev-parse HEAD)x" "xfsazqfxcadas" "Xfsazqfxcadas"
+ do
+ printf "%s" $bad_content >$worktree1_refdir_prefix/bad-branch-1 &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: worktrees/worktree-1/refs/worktree/bad-branch-1: badRefContent: $bad_content
+ EOF
+ rm $worktree1_refdir_prefix/bad-branch-1 &&
+ test_cmp expect err || return 1
+ done &&
+
+ for bad_content in "$(git rev-parse HEAD)x" "xfsazqfxcadas" "Xfsazqfxcadas"
+ do
+ printf "%s" $bad_content >$worktree2_refdir_prefix/bad-branch-2 &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: worktrees/worktree-2/refs/worktree/bad-branch-2: badRefContent: $bad_content
+ EOF
+ rm $worktree2_refdir_prefix/bad-branch-2 &&
+ test_cmp expect err || return 1
+ done &&
+
+ printf "%s" "$(git rev-parse HEAD)" >$worktree1_refdir_prefix/branch-no-newline &&
+ git refs verify 2>err &&
cat >expect <<-EOF &&
- warning: worktrees/worktree-1/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
- error: worktrees/worktree-1/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''worktrees/worktree-1/refs/worktree/$bad_referent_name'\''
+ warning: worktrees/worktree-1/refs/worktree/branch-no-newline: refMissingNewline: misses LF at the end
EOF
- rm $worktree1_refdir_prefix/bad-symbolic &&
+ rm $worktree1_refdir_prefix/branch-no-newline &&
test_cmp expect err &&
- ln -sf ../../../../refs/heads/"$bad_referent_name" $worktree1_refdir_prefix/bad-symbolic &&
- test_must_fail git refs verify 2>err &&
+ printf "%s garbage" "$(git rev-parse HEAD)" >$worktree1_refdir_prefix/branch-garbage &&
+ git refs verify 2>err &&
cat >expect <<-EOF &&
- warning: worktrees/worktree-1/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
- error: worktrees/worktree-1/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''refs/heads/$bad_referent_name'\''
+ warning: worktrees/worktree-1/refs/worktree/branch-garbage: trailingRefContent: has trailing garbage: '\'' garbage'\''
EOF
- rm $worktree1_refdir_prefix/bad-symbolic &&
- test_cmp expect err &&
+ rm $worktree1_refdir_prefix/branch-garbage &&
+ test_cmp expect err
+ )
+'
- ln -sf ./"$bad_referent_name" $worktree2_refdir_prefix/bad-symbolic &&
+test_expect_success SYMLINKS 'the filetype of packed-refs should be checked' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit default &&
+ git branch branch-1 &&
+ git branch branch-2 &&
+ git branch branch-3 &&
+ git pack-refs --all &&
+
+ mv .git/packed-refs .git/packed-refs-back &&
+ ln -sf packed-refs-back .git/packed-refs &&
test_must_fail git refs verify 2>err &&
cat >expect <<-EOF &&
- warning: worktrees/worktree-2/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
- error: worktrees/worktree-2/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''worktrees/worktree-2/refs/worktree/$bad_referent_name'\''
+ error: packed-refs: badRefFiletype: not a regular file but a symlink
EOF
- rm $worktree2_refdir_prefix/bad-symbolic &&
+ rm .git/packed-refs &&
test_cmp expect err &&
- ln -sf ../../../../refs/heads/"$bad_referent_name" $worktree2_refdir_prefix/bad-symbolic &&
+ mkdir .git/packed-refs &&
test_must_fail git refs verify 2>err &&
cat >expect <<-EOF &&
- warning: worktrees/worktree-2/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
- error: worktrees/worktree-2/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''refs/heads/$bad_referent_name'\''
+ error: packed-refs: badRefFiletype: not a regular file
EOF
- rm $worktree2_refdir_prefix/bad-symbolic &&
- test_cmp expect err || return 1
- done
+ rm -r .git/packed-refs &&
+ test_cmp expect err
+ )
'
-test_expect_success 'ref content checks should work with worktrees' '
+test_expect_success 'packed-refs header should be checked' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit default &&
+
+ git refs verify 2>err &&
+ test_must_be_empty err &&
+
+ for bad_header in "# pack-refs wit: peeled fully-peeled sorted " \
+ "# pack-refs with traits: peeled fully-peeled sorted " \
+ "# pack-refs with a: peeled fully-peeled" \
+ "# pack-refs with:peeled fully-peeled sorted"
+ do
+ printf "%s\n" "$bad_header" >.git/packed-refs &&
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: packed-refs.header: badPackedRefHeader: '\''$bad_header'\'' does not start with '\''# pack-refs with: '\''
+ EOF
+ rm .git/packed-refs &&
+ test_cmp expect err || return 1
+ done
+ )
+'
+
+test_expect_success 'packed-refs missing header should not be reported' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit default &&
+
+ printf "$(git rev-parse HEAD) refs/heads/main\n" >.git/packed-refs &&
+ git refs verify 2>err &&
+ test_must_be_empty err
+ )
+'
+
+test_expect_success 'packed-refs unknown traits should not be reported' '
test_when_finished "rm -rf repo" &&
git init repo &&
- cd repo &&
- test_commit default &&
- git branch branch-1 &&
- git branch branch-2 &&
- git branch branch-3 &&
- git worktree add ./worktree-1 branch-2 &&
- git worktree add ./worktree-2 branch-3 &&
- worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
- worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
+ (
+ cd repo &&
+ test_commit default &&
+
+ printf "# pack-refs with: peeled fully-peeled sorted foo\n" >.git/packed-refs &&
+ git refs verify 2>err &&
+ test_must_be_empty err
+ )
+'
+test_expect_success 'packed-refs content should be checked' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
(
- cd worktree-1 &&
- git update-ref refs/worktree/branch-4 refs/heads/branch-1
- ) &&
+ cd repo &&
+ test_commit default &&
+ git branch branch-1 &&
+ git branch branch-2 &&
+ git tag -a annotated-tag-1 -m tag-1 &&
+ git tag -a annotated-tag-2 -m tag-2 &&
+
+ branch_1_oid=$(git rev-parse branch-1) &&
+ branch_2_oid=$(git rev-parse branch-2) &&
+ tag_1_oid=$(git rev-parse annotated-tag-1) &&
+ tag_2_oid=$(git rev-parse annotated-tag-2) &&
+ tag_1_peeled_oid=$(git rev-parse annotated-tag-1^{}) &&
+ tag_2_peeled_oid=$(git rev-parse annotated-tag-2^{}) &&
+ short_oid=$(printf "%s" $tag_1_peeled_oid | cut -c 1-4) &&
+
+ cat >.git/packed-refs <<-EOF &&
+ # pack-refs with: peeled fully-peeled sorted
+ $short_oid refs/heads/branch-1
+ ${branch_1_oid}x
+ $branch_2_oid refs/heads/bad-branch
+ $branch_2_oid refs/heads/branch.
+ $tag_1_oid refs/tags/annotated-tag-3
+ ^$short_oid
+ $tag_2_oid refs/tags/annotated-tag-4.
+ ^$tag_2_peeled_oid garbage
+ EOF
+ test_must_fail git refs verify 2>err &&
+ cat >expect <<-EOF &&
+ error: packed-refs line 2: badPackedRefEntry: '\''$short_oid refs/heads/branch-1'\'' has invalid oid
+ error: packed-refs line 3: badPackedRefEntry: has no space after oid '\''$branch_1_oid'\'' but with '\''x'\''
+ error: packed-refs line 4: badRefName: has bad refname '\'' refs/heads/bad-branch'\''
+ error: packed-refs line 5: badRefName: has bad refname '\''refs/heads/branch.'\''
+ error: packed-refs line 7: badPackedRefEntry: '\''$short_oid'\'' has invalid peeled oid
+ error: packed-refs line 8: badRefName: has bad refname '\''refs/tags/annotated-tag-4.'\''
+ error: packed-refs line 9: badPackedRefEntry: has trailing garbage after peeled oid '\'' garbage'\''
+ EOF
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'packed-ref with sorted trait should be checked' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
(
- cd worktree-2 &&
- git update-ref refs/worktree/branch-4 refs/heads/branch-1
- ) &&
+ cd repo &&
+ test_commit default &&
+ git branch branch-1 &&
+ git branch branch-2 &&
+ git tag -a annotated-tag-1 -m tag-1 &&
+ branch_1_oid=$(git rev-parse branch-1) &&
+ branch_2_oid=$(git rev-parse branch-2) &&
+ tag_1_oid=$(git rev-parse annotated-tag-1) &&
+ tag_1_peeled_oid=$(git rev-parse annotated-tag-1^{}) &&
+ refname1="refs/heads/main" &&
+ refname2="refs/heads/foo" &&
+ refname3="refs/tags/foo" &&
+
+ cat >.git/packed-refs <<-EOF &&
+ # pack-refs with: peeled fully-peeled sorted
+ EOF
+ git refs verify 2>err &&
+ rm .git/packed-refs &&
+ test_must_be_empty err &&
- for bad_content in "$(git rev-parse HEAD)x" "xfsazqfxcadas" "Xfsazqfxcadas"
- do
- printf "%s" $bad_content >$worktree1_refdir_prefix/bad-branch-1 &&
+ cat >.git/packed-refs <<-EOF &&
+ # pack-refs with: peeled fully-peeled sorted
+ $branch_2_oid $refname1
+ EOF
+ git refs verify 2>err &&
+ rm .git/packed-refs &&
+ test_must_be_empty err &&
+
+ cat >.git/packed-refs <<-EOF &&
+ # pack-refs with: peeled fully-peeled sorted
+ $branch_2_oid $refname1
+ $branch_1_oid $refname2
+ $tag_1_oid $refname3
+ EOF
test_must_fail git refs verify 2>err &&
cat >expect <<-EOF &&
- error: worktrees/worktree-1/refs/worktree/bad-branch-1: badRefContent: $bad_content
+ error: packed-refs line 3: packedRefUnsorted: refname '\''$refname2'\'' is less than previous refname '\''$refname1'\''
EOF
- rm $worktree1_refdir_prefix/bad-branch-1 &&
- test_cmp expect err || return 1
- done &&
+ rm .git/packed-refs &&
+ test_cmp expect err &&
- for bad_content in "$(git rev-parse HEAD)x" "xfsazqfxcadas" "Xfsazqfxcadas"
- do
- printf "%s" $bad_content >$worktree2_refdir_prefix/bad-branch-2 &&
+ cat >.git/packed-refs <<-EOF &&
+ # pack-refs with: peeled fully-peeled sorted
+ $tag_1_oid $refname3
+ ^$tag_1_peeled_oid
+ $branch_2_oid $refname2
+ EOF
test_must_fail git refs verify 2>err &&
cat >expect <<-EOF &&
- error: worktrees/worktree-2/refs/worktree/bad-branch-2: badRefContent: $bad_content
- EOF
- rm $worktree2_refdir_prefix/bad-branch-2 &&
- test_cmp expect err || return 1
- done &&
-
- printf "%s" "$(git rev-parse HEAD)" >$worktree1_refdir_prefix/branch-no-newline &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: worktrees/worktree-1/refs/worktree/branch-no-newline: refMissingNewline: misses LF at the end
- EOF
- rm $worktree1_refdir_prefix/branch-no-newline &&
- test_cmp expect err &&
-
- printf "%s garbage" "$(git rev-parse HEAD)" >$worktree1_refdir_prefix/branch-garbage &&
- git refs verify 2>err &&
- cat >expect <<-EOF &&
- warning: worktrees/worktree-1/refs/worktree/branch-garbage: trailingRefContent: has trailing garbage: '\'' garbage'\''
- EOF
- rm $worktree1_refdir_prefix/branch-garbage &&
- test_cmp expect err
+ error: packed-refs line 4: packedRefUnsorted: refname '\''$refname2'\'' is less than previous refname '\''$refname3'\''
+ EOF
+ rm .git/packed-refs &&
+ test_cmp expect err
+ )
+'
+
+test_expect_success 'packed-ref without sorted trait should not be checked' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit default &&
+ git branch branch-1 &&
+ git branch branch-2 &&
+ git tag -a annotated-tag-1 -m tag-1 &&
+ branch_1_oid=$(git rev-parse branch-1) &&
+ branch_2_oid=$(git rev-parse branch-2) &&
+ tag_1_oid=$(git rev-parse annotated-tag-1) &&
+ tag_1_peeled_oid=$(git rev-parse annotated-tag-1^{}) &&
+ refname1="refs/heads/main" &&
+ refname2="refs/heads/foo" &&
+ refname3="refs/tags/foo" &&
+
+ cat >.git/packed-refs <<-EOF &&
+ # pack-refs with: peeled fully-peeled
+ $branch_2_oid $refname1
+ $branch_1_oid $refname2
+ EOF
+ git refs verify 2>err &&
+ test_must_be_empty err
+ )
+'
+
+test_expect_success '--[no-]references option should apply to fsck' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ branch_dir_prefix=.git/refs/heads &&
+ (
+ cd repo &&
+ test_commit default &&
+ for trailing_content in " garbage" " more garbage"
+ do
+ printf "%s" "$(git rev-parse HEAD)$trailing_content" >$branch_dir_prefix/branch-garbage &&
+ git fsck 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\''$trailing_content'\''
+ EOF
+ rm $branch_dir_prefix/branch-garbage &&
+ test_cmp expect err || return 1
+ done &&
+
+ for trailing_content in " garbage" " more garbage"
+ do
+ printf "%s" "$(git rev-parse HEAD)$trailing_content" >$branch_dir_prefix/branch-garbage &&
+ git fsck --references 2>err &&
+ cat >expect <<-EOF &&
+ warning: refs/heads/branch-garbage: trailingRefContent: has trailing garbage: '\''$trailing_content'\''
+ EOF
+ rm $branch_dir_prefix/branch-garbage &&
+ test_cmp expect err || return 1
+ done &&
+
+ for trailing_content in " garbage" " more garbage"
+ do
+ printf "%s" "$(git rev-parse HEAD)$trailing_content" >$branch_dir_prefix/branch-garbage &&
+ git fsck --no-references 2>err &&
+ rm $branch_dir_prefix/branch-garbage &&
+ test_must_be_empty err || return 1
+ done
+ )
'
test_done
diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
index 4618ffc108..002a75dee8 100755
--- a/t/t0610-reftable-basics.sh
+++ b/t/t0610-reftable-basics.sh
@@ -14,6 +14,13 @@ export GIT_TEST_DEFAULT_REF_FORMAT
INVALID_OID=$(test_oid 001)
+test_expect_success 'pack-refs does not crash with -h' '
+ test_expect_code 129 git pack-refs -h >usage &&
+ test_grep "[Uu]sage: git pack-refs " usage &&
+ test_expect_code 129 nongit git pack-refs -h >usage &&
+ test_grep "[Uu]sage: git pack-refs " usage
+'
+
test_expect_success 'init: creates basic reftable structures' '
test_when_finished "rm -rf repo" &&
git init repo &&
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index 398865d6eb..86a2825473 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -903,6 +903,59 @@ test_expect_success 'cat-file -t and -s on corrupt loose object' '
)
'
+test_expect_success 'truncated object with --allow-unknown-type' - <<\EOT
+ objtype='a really long type name that exceeds the 32-byte limit' &&
+ blob=$(git hash-object -w --literally -t "$objtype" /dev/null) &&
+ objpath=.git/objects/$(test_oid_to_path "$blob") &&
+
+ # We want to truncate the object far enough in that we don't hit the
+ # end while inflating the first 32 bytes (since we want to have to dig
+ # for the trailing NUL of the header). But we don't want to go too far,
+ # since our header isn't very big. And of course we are counting
+ # deflated zlib bytes in the on-disk file, so it's a bit of a guess.
+ # Empirically 50 seems to work.
+ mv "$objpath" obj.bak &&
+ test_when_finished 'mv obj.bak "$objpath"' &&
+ test_copy_bytes 50 <obj.bak >"$objpath" &&
+
+ test_must_fail git cat-file --allow-unknown-type -t $blob 2>err &&
+ test_grep "unable to unpack $blob header" err
+EOT
+
+test_expect_success 'object reading handles zlib dictionary' - <<\EOT
+ echo 'content that will be recompressed' >file &&
+ blob=$(git hash-object -w file) &&
+ objpath=.git/objects/$(test_oid_to_path "$blob") &&
+
+ # Recompress a loose object using a precomputed zlib dictionary.
+ # This was originally done with:
+ #
+ # perl -MCompress::Raw::Zlib -e '
+ # binmode STDIN;
+ # binmode STDOUT;
+ # my $data = do { local $/; <STDIN> };
+ # my $in = new Compress::Raw::Zlib::Inflate;
+ # my $de = new Compress::Raw::Zlib::Deflate(
+ # -Dictionary => "anything"
+ # );
+ # $in->inflate($data, $raw);
+ # $de->deflate($raw, $out);
+ # print $out;
+ # ' <obj.bak >$objpath
+ #
+ # but we do not want to require the perl module for all test runs (nor
+ # carry a custom t/helper program that uses zlib features we don't
+ # otherwise care about).
+ mv "$objpath" obj.bak &&
+ test_when_finished 'mv obj.bak "$objpath"' &&
+ printf '\170\273\017\112\003\143' >$objpath &&
+
+ test_must_fail git cat-file blob $blob 2>err &&
+ test_grep ! 'too long' err &&
+ test_grep 'error: unable to unpack' err &&
+ test_grep 'error: inflate: needs dictionary' err
+EOT
+
# Tests for git cat-file --follow-symlinks
test_expect_success 'prep for symlink tests' '
echo_without_newline "$hello_content" >morx &&
diff --git a/t/t1419-exclude-refs.sh b/t/t1419-exclude-refs.sh
index c04eeb7211..04797aee59 100755
--- a/t/t1419-exclude-refs.sh
+++ b/t/t1419-exclude-refs.sh
@@ -46,6 +46,10 @@ test_expect_success 'setup' '
echo "create refs/heads/$name/$i $base" || return 1
done || return 1
done >in &&
+ for i in 5 6 7
+ do
+ echo "create refs/heads/bar/4/$i $base" || return 1
+ done >>in &&
echo "delete refs/heads/main" >>in &&
git update-ref --stdin <in &&
@@ -99,9 +103,17 @@ test_expect_success 'adjacent, non-overlapping excluded regions' '
esac
'
-test_expect_success 'overlapping excluded regions' '
+test_expect_success 'non-directory excluded regions' '
for_each_ref__exclude refs/heads refs/heads/ba refs/heads/baz >actual 2>perf &&
- for_each_ref refs/heads/foo refs/heads/quux >expect &&
+ for_each_ref refs/heads/bar refs/heads/foo refs/heads/quux >expect &&
+
+ test_cmp expect actual &&
+ assert_jumps 1 perf
+'
+
+test_expect_success 'overlapping excluded regions' '
+ for_each_ref__exclude refs/heads refs/heads/bar refs/heads/bar/4 >actual 2>perf &&
+ for_each_ref refs/heads/baz refs/heads/foo refs/heads/quux >expect &&
test_cmp expect actual &&
assert_jumps 1 perf
@@ -155,4 +167,14 @@ test_expect_success 'meta-characters are discarded' '
assert_no_jumps perf
'
+test_expect_success 'empty string exclude pattern is ignored' '
+ git update-ref refs/heads/loose $(git rev-parse refs/heads/foo/1) &&
+
+ for_each_ref__exclude refs/heads "" >actual 2>perf &&
+ for_each_ref >expect &&
+
+ test_cmp expect actual &&
+ assert_no_jumps perf
+'
+
test_done
diff --git a/t/t2006-checkout-index-basic.sh b/t/t2006-checkout-index-basic.sh
index bac231b167..fedd2cc097 100755
--- a/t/t2006-checkout-index-basic.sh
+++ b/t/t2006-checkout-index-basic.sh
@@ -21,6 +21,13 @@ test_expect_success 'checkout-index -h in broken repository' '
test_grep "[Uu]sage" broken/usage
'
+test_expect_success 'checkout-index does not crash with -h' '
+ test_expect_code 129 git checkout-index -h >usage &&
+ test_grep "[Uu]sage: git checkout-index " usage &&
+ test_expect_code 129 nongit git checkout-index -h >usage &&
+ test_grep "[Uu]sage: git checkout-index " usage
+'
+
test_expect_success 'checkout-index reports errors (cmdline)' '
test_must_fail git checkout-index -- does-not-exist 2>stderr &&
test_grep not.in.the.cache stderr
diff --git a/t/t3004-ls-files-basic.sh b/t/t3004-ls-files-basic.sh
index a1078f8701..4034a5a59f 100755
--- a/t/t3004-ls-files-basic.sh
+++ b/t/t3004-ls-files-basic.sh
@@ -34,6 +34,13 @@ test_expect_success 'ls-files -h in corrupt repository' '
test_grep "[Uu]sage: git ls-files " broken/usage
'
+test_expect_success 'ls-files does not crash with -h' '
+ test_expect_code 129 git ls-files -h >usage &&
+ test_grep "[Uu]sage: git ls-files " usage &&
+ test_expect_code 129 nongit git ls-files -h >usage &&
+ test_grep "[Uu]sage: git ls-files " usage
+'
+
test_expect_success SYMLINKS 'ls-files with absolute paths to symlinks' '
mkdir subs &&
ln -s nosuch link &&
diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
index 389670262e..58b3759935 100755
--- a/t/t3650-replay-basics.sh
+++ b/t/t3650-replay-basics.sh
@@ -195,4 +195,26 @@ test_expect_success 'using replay on bare repo to rebase multiple divergent bran
done
'
+test_expect_success 'merge.directoryRenames=false' '
+ # create a test case that stress-tests the rename caching
+ git switch -c rename-onto &&
+
+ mkdir -p to-rename &&
+ test_commit to-rename/move &&
+
+ mkdir -p renamed-directory &&
+ git mv to-rename/move* renamed-directory/ &&
+ test_tick &&
+ git commit -m renamed-directory &&
+
+ git switch -c rename-from HEAD^ &&
+ test_commit to-rename/add-a-file &&
+ echo modified >to-rename/add-a-file.t &&
+ test_tick &&
+ git commit -m modified to-rename/add-a-file.t &&
+
+ git -c merge.directoryRenames=false replay \
+ --onto rename-onto rename-onto..rename-from
+'
+
test_done
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index f7ff234cf9..ec2804eea6 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -89,4 +89,14 @@ test_expect_success '-U0 is valid, so is diff.context=0' '
grep "^+MODIFIED" output
'
+test_expect_success '-U2147483647 works' '
+ echo APPENDED >>x &&
+ test_line_count = 16 x &&
+ git diff -U2147483647 >output &&
+ test_line_count = 22 output &&
+ grep "^-ADDED" output &&
+ grep "^+MODIFIED" output &&
+ grep "^+APPENDED" output
+'
+
test_done
diff --git a/t/t4070-diff-pairs.sh b/t/t4070-diff-pairs.sh
new file mode 100755
index 0000000000..70deafb860
--- /dev/null
+++ b/t/t4070-diff-pairs.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+test_description='basic diff-pairs tests'
+. ./test-lib.sh
+
+# This creates a diff with added, modified, deleted, renamed, copied, and
+# typechange entries. This includes a submodule to test submodule diff support.
+test_expect_success 'setup' '
+ test_config_global protocol.file.allow always &&
+ git init sub &&
+ test_commit -C sub initial &&
+
+ git init main &&
+ cd main &&
+ echo to-be-gone >deleted &&
+ echo original >modified &&
+ echo now-a-file >symlink &&
+ test_seq 200 >two-hundred &&
+ test_seq 201 500 >five-hundred &&
+ git add . &&
+ test_tick &&
+ git commit -m base &&
+ git tag base &&
+
+ git submodule add ../sub &&
+ echo now-here >added &&
+ echo new >modified &&
+ rm deleted &&
+ mkdir subdir &&
+ echo content >subdir/file &&
+ mv two-hundred renamed &&
+ test_seq 201 500 | sed s/300/modified/ >copied &&
+ rm symlink &&
+ git add -A . &&
+ test_ln_s_add dest symlink &&
+ test_tick &&
+ git commit -m new &&
+ git tag new
+'
+
+test_expect_success 'diff-pairs recreates --raw' '
+ git diff-tree -r -M -C -C -z base new >expect &&
+ git diff-pairs --raw -z >actual <expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'diff-pairs can create -p output' '
+ git diff-tree -p -M -C -C base new >expect &&
+ git diff-tree -r -M -C -C -z base new |
+ git diff-pairs -p -z >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'diff-pairs does not support normal raw diff input' '
+ git diff-tree -r base new |
+ test_must_fail git diff-pairs >out 2>err &&
+
+ echo "usage: working without -z is not supported" >expect &&
+ test_must_be_empty out &&
+ test_cmp expect err
+'
+
+test_expect_success 'diff-pairs does not support tree objects as input' '
+ git diff-tree -z base new |
+ test_must_fail git diff-pairs -z >out 2>err &&
+
+ echo "fatal: tree objects not supported" >expect &&
+ test_must_be_empty out &&
+ test_cmp expect err
+'
+
+test_expect_success 'diff-pairs does not support pathspec arguments' '
+ git diff-tree -r -z base new |
+ test_must_fail git diff-pairs -z -- new >out 2>err &&
+
+ echo "usage: pathspec arguments not supported" >expect &&
+ test_must_be_empty out &&
+ test_cmp expect err
+'
+
+test_expect_success 'diff-pairs explicit queue flush' '
+ git diff-tree -r -M -C -C -z base new >expect &&
+ printf "\0" >>expect &&
+ git diff-tree -r -M -C -C -z base new >>expect &&
+
+ git diff-pairs --raw -z <expect >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index edb38da701..8e1ecf8a68 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -112,7 +112,7 @@ test_expect_success 'am --abort will keep dirty index intact' '
test_expect_success 'am -3 stops on conflict on unborn branch' '
git checkout -f --orphan orphan &&
git reset &&
- rm -f otherfile-4 &&
+ rm -f file-1 otherfile-4 &&
test_must_fail git am -3 0003-*.patch &&
test 2 -eq $(git ls-files -u | wc -l) &&
test 4 = "$(cat otherfile-4)"
diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
index bcab71c8e8..190c484321 100755
--- a/t/t4206-log-follow-harder-copies.sh
+++ b/t/t4206-log-follow-harder-copies.sh
@@ -54,4 +54,36 @@ test_expect_success 'validate the output.' '
compare_diff_patch current expected
'
+test_expect_success 'log --follow -B does not BUG' '
+ git switch --orphan break_and_follow_are_icky_so_use_both &&
+
+ test_seq 1 127 >numbers &&
+ git add numbers &&
+ git commit -m "numbers" &&
+
+ printf "%s\n" A B C D E F G H I J K L M N O Q R S T U V W X Y Z >pool &&
+ echo changed >numbers &&
+ git add pool numbers &&
+ git commit -m "pool" &&
+
+ git log -1 -B --raw --follow -- "p*"
+'
+
+test_expect_success 'log --follow -B does not die or use uninitialized memory' '
+ printf "%s\n" A B C D E F G H I J K L M N O P Q R S T U V W X Y Z >z &&
+ git add z &&
+ git commit -m "Initial" &&
+
+ test_seq 1 130 >z &&
+ echo lame >somefile &&
+ git add z somefile &&
+ git commit -m "Rewrite z, introduce lame somefile" &&
+
+ echo Content >somefile &&
+ git add somefile &&
+ git commit -m "Rewrite somefile" &&
+
+ git log -B --follow somefile
+'
+
test_done
diff --git a/t/t4255-am-submodule.sh b/t/t4255-am-submodule.sh
index a7ba08f728..e6679a01b4 100755
--- a/t/t4255-am-submodule.sh
+++ b/t/t4255-am-submodule.sh
@@ -19,7 +19,6 @@ am_3way () {
$2 git am --3way patch
}
-KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
test_submodule_switch_func "am_3way"
test_expect_success 'setup diff.submodule' '
diff --git a/t/t4301-merge-tree-write-tree.sh b/t/t4301-merge-tree-write-tree.sh
index eea19907b5..44f7d07759 100755
--- a/t/t4301-merge-tree-write-tree.sh
+++ b/t/t4301-merge-tree-write-tree.sh
@@ -73,6 +73,12 @@ test_expect_success 'Clean merge' '
test_cmp expect actual
'
+# Repeat the previous test, but turn off rename detection
+test_expect_success 'Failed merge without rename detection' '
+ test_must_fail git -c diff.renames=false merge-tree --write-tree side1 side3 >out &&
+ grep "CONFLICT (modify/delete): numbers deleted" out
+'
+
test_expect_success 'Content merge and a few conflicts' '
git checkout side1^0 &&
test_must_fail git merge side2 &&
diff --git a/t/t5323-pack-redundant.sh b/t/t5323-pack-redundant.sh
index 688cd9706c..bc30bc9652 100755
--- a/t/t5323-pack-redundant.sh
+++ b/t/t5323-pack-redundant.sh
@@ -36,7 +36,7 @@ relationship between packs and objects is as follows:
. ./test-lib.sh
-if ! test_have_prereq WITHOUT_BREAKING_CHANGES
+if test_have_prereq WITH_BREAKING_CHANGES
then
skip_all='skipping git-pack-redundant tests; built with breaking changes'
test_done
diff --git a/t/t5329-pack-objects-cruft.sh b/t/t5329-pack-objects-cruft.sh
index b71a0aef40..25ddda5cf3 100755
--- a/t/t5329-pack-objects-cruft.sh
+++ b/t/t5329-pack-objects-cruft.sh
@@ -360,43 +360,6 @@ test_expect_success 'expired objects are pruned' '
)
'
-test_expect_success 'repack --cruft generates a cruft pack' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit reachable &&
- git branch -M main &&
- git checkout --orphan other &&
- test_commit unreachable &&
-
- git checkout main &&
- git branch -D other &&
- git tag -d unreachable &&
- # objects are not cruft if they are contained in the reflogs
- git reflog expire --all --expire=all &&
-
- git rev-list --objects --all --no-object-names >reachable.raw &&
- git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
- sort <reachable.raw >reachable &&
- comm -13 reachable objects >unreachable &&
-
- git repack --cruft -d &&
-
- cruft=$(basename $(ls $packdir/pack-*.mtimes) .mtimes) &&
- pack=$(basename $(ls $packdir/pack-*.pack | grep -v $cruft) .pack) &&
-
- git show-index <$packdir/$pack.idx >actual.raw &&
- cut -f2 -d" " actual.raw | sort >actual &&
- test_cmp reachable actual &&
-
- git show-index <$packdir/$cruft.idx >actual.raw &&
- cut -f2 -d" " actual.raw | sort >actual &&
- test_cmp unreachable actual
- )
-'
-
test_expect_success 'loose objects mtimes upsert others' '
git init repo &&
test_when_finished "rm -fr repo" &&
@@ -470,219 +433,6 @@ test_expect_success 'expiring cruft objects with git gc' '
)
'
-test_expect_success 'cruft packs are not included in geometric repack' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit reachable &&
- git repack -Ad &&
- git branch -M main &&
-
- git checkout --orphan other &&
- test_commit cruft &&
- git repack -d &&
-
- git checkout main &&
- git branch -D other &&
- git tag -d cruft &&
- git reflog expire --all --expire=all &&
-
- git repack --cruft &&
-
- find $packdir -type f | sort >before &&
- git repack --geometric=2 -d &&
- find $packdir -type f | sort >after &&
-
- test_cmp before after
- )
-'
-
-test_expect_success 'repack --geometric collects once-cruft objects' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit reachable &&
- git repack -Ad &&
- git branch -M main &&
-
- git checkout --orphan other &&
- git rm -rf . &&
- test_commit --no-tag cruft &&
- cruft="$(git rev-parse HEAD)" &&
-
- git checkout main &&
- git branch -D other &&
- git reflog expire --all --expire=all &&
-
- # Pack the objects created in the previous step into a cruft
- # pack. Intentionally leave loose copies of those objects
- # around so we can pick them up in a subsequent --geometric
- # reapack.
- git repack --cruft &&
-
- # Now make those objects reachable, and ensure that they are
- # packed into the new pack created via a --geometric repack.
- git update-ref refs/heads/other $cruft &&
-
- # Without this object, the set of unpacked objects is exactly
- # the set of objects already in the cruft pack. Tweak that set
- # to ensure we do not overwrite the cruft pack entirely.
- test_commit reachable2 &&
-
- find $packdir -name "pack-*.idx" | sort >before &&
- git repack --geometric=2 -d &&
- find $packdir -name "pack-*.idx" | sort >after &&
-
- {
- git rev-list --objects --no-object-names $cruft &&
- git rev-list --objects --no-object-names reachable..reachable2
- } >want.raw &&
- sort want.raw >want &&
-
- pack=$(comm -13 before after) &&
- git show-index <$pack >objects.raw &&
-
- cut -d" " -f2 objects.raw | sort >got &&
-
- test_cmp want got
- )
-'
-
-test_expect_success 'cruft repack with no reachable objects' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit base &&
- git repack -ad &&
-
- base="$(git rev-parse base)" &&
-
- git for-each-ref --format="delete %(refname)" >in &&
- git update-ref --stdin <in &&
- git reflog expire --all --expire=all &&
- rm -fr .git/index &&
-
- git repack --cruft -d &&
-
- git cat-file -t $base
- )
-'
-
-write_blob () {
- test-tool genrandom "$@" >in &&
- git hash-object -w -t blob in
-}
-
-find_pack () {
- for idx in $(ls $packdir/pack-*.idx)
- do
- git show-index <$idx >out &&
- if grep -q "$1" out
- then
- echo $idx
- fi || return 1
- done
-}
-
-test_expect_success 'cruft repack with --max-pack-size' '
- git init max-pack-size &&
- (
- cd max-pack-size &&
- test_commit base &&
-
- # two cruft objects which exceed the maximum pack size
- foo=$(write_blob foo 1048576) &&
- bar=$(write_blob bar 1048576) &&
- test-tool chmtime --get -1000 \
- "$objdir/$(test_oid_to_path $foo)" >foo.mtime &&
- test-tool chmtime --get -2000 \
- "$objdir/$(test_oid_to_path $bar)" >bar.mtime &&
- git repack --cruft --max-pack-size=1M &&
- find $packdir -name "*.mtimes" >cruft &&
- test_line_count = 2 cruft &&
-
- foo_mtimes="$(basename $(find_pack $foo) .idx).mtimes" &&
- bar_mtimes="$(basename $(find_pack $bar) .idx).mtimes" &&
- test-tool pack-mtimes $foo_mtimes >foo.actual &&
- test-tool pack-mtimes $bar_mtimes >bar.actual &&
-
- echo "$foo $(cat foo.mtime)" >foo.expect &&
- echo "$bar $(cat bar.mtime)" >bar.expect &&
-
- test_cmp foo.expect foo.actual &&
- test_cmp bar.expect bar.actual &&
- test "$foo_mtimes" != "$bar_mtimes"
- )
-'
-
-test_expect_success 'cruft repack with pack.packSizeLimit' '
- (
- cd max-pack-size &&
- # repack everything back together to remove the existing cruft
- # pack (but to keep its objects)
- git repack -adk &&
- git -c pack.packSizeLimit=1M repack --cruft &&
- # ensure the same post condition is met when --max-pack-size
- # would otherwise be inferred from the configuration
- find $packdir -name "*.mtimes" >cruft &&
- test_line_count = 2 cruft &&
- for pack in $(cat cruft)
- do
- test-tool pack-mtimes "$(basename $pack)" >objects &&
- test_line_count = 1 objects || return 1
- done
- )
-'
-
-test_expect_success 'cruft repack respects repack.cruftWindow' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit base &&
-
- GIT_TRACE2_EVENT=$(pwd)/event.trace \
- git -c pack.window=1 -c repack.cruftWindow=2 repack \
- --cruft --window=3 &&
-
- grep "pack-objects.*--window=2.*--cruft" event.trace
- )
-'
-
-test_expect_success 'cruft repack respects --window by default' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit base &&
-
- GIT_TRACE2_EVENT=$(pwd)/event.trace \
- git -c pack.window=2 repack --cruft --window=3 &&
-
- grep "pack-objects.*--window=3.*--cruft" event.trace
- )
-'
-
-test_expect_success 'cruft repack respects --quiet' '
- git init repo &&
- test_when_finished "rm -fr repo" &&
- (
- cd repo &&
-
- test_commit base &&
- GIT_PROGRESS_DELAY=0 git repack --cruft --quiet 2>err &&
- test_must_be_empty err
- )
-'
-
test_expect_success 'cruft --local drops unreachable objects' '
git init alternate &&
git init repo &&
@@ -945,4 +695,56 @@ test_expect_success 'additional cruft blobs via gc.recentObjectsHook' '
)
'
+test_expect_success 'split cruft packs with --max-cruft-size' '
+ repo=cruft-with--max-cruft-size &&
+ test_when_finished "rm -fr $repo" &&
+
+ git init "$repo" &&
+
+ (
+ cd "$repo" &&
+
+ git config core.compression 0 &&
+
+ sz=$((1024 * 1024)) && # 1MiB
+ test-tool genrandom foo $sz >foo &&
+ test-tool genrandom bar $sz >bar &&
+ foo="$(git hash-object -w -t blob foo)" &&
+ bar="$(git hash-object -w -t blob bar)" &&
+
+ to=$packdir/pack &&
+ # Pack together foo and bar into a single 2MiB pack.
+ pack="$(git pack-objects $to <<-EOF
+ $foo
+ $bar
+ EOF
+ )" &&
+
+ # Then generate a cruft pack containing foo and bar.
+ #
+ # Generate the pack with --max-pack-size equal to the
+ # size of one object, forcing us to write two cruft
+ # packs.
+ git pack-objects --cruft --max-pack-size=$sz $to <<-EOF &&
+ -pack-$pack.pack
+ EOF
+
+ ls $packdir/pack-*.mtimes >crufts &&
+ test_line_count = 2 crufts &&
+
+ for cruft in $(cat crufts)
+ do
+ test-tool pack-mtimes "$(basename "$cruft")" || return 1
+ done >actual.raw &&
+
+ cut -d" " -f1 <actual.raw | sort >actual &&
+ sort >expect <<-EOF &&
+ $foo
+ $bar
+ EOF
+
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t5334-incremental-multi-pack-index.sh b/t/t5334-incremental-multi-pack-index.sh
index 26257e5660..d30d7253d6 100755
--- a/t/t5334-incremental-multi-pack-index.sh
+++ b/t/t5334-incremental-multi-pack-index.sh
@@ -44,4 +44,91 @@ test_expect_success 'convert incremental to non-incremental' '
compare_results_with_midx 'non-incremental MIDX conversion'
+write_midx_layer () {
+ n=1
+ if test -f $midx_chain
+ then
+ n="$(($(wc -l <$midx_chain) + 1))"
+ fi
+
+ for i in 1 2
+ do
+ test_commit $n.$i &&
+ git repack -d || return 1
+ done &&
+ git multi-pack-index write --bitmap --incremental
+}
+
+test_expect_success 'write initial MIDX layer' '
+ git repack -ad &&
+ write_midx_layer
+'
+
+test_expect_success 'read bitmap from first MIDX layer' '
+ git rev-list --test-bitmap 1.2
+'
+
+test_expect_success 'write another MIDX layer' '
+ write_midx_layer
+'
+
+test_expect_success 'midx verify with multiple layers' '
+ test_path_is_file "$midx_chain" &&
+ test_line_count = 2 "$midx_chain" &&
+
+ git multi-pack-index verify
+'
+
+test_expect_success 'read bitmap from second MIDX layer' '
+ git rev-list --test-bitmap 2.2
+'
+
+test_expect_success 'read earlier bitmap from second MIDX layer' '
+ git rev-list --test-bitmap 1.2
+'
+
+test_expect_success 'show object from first pack' '
+ git cat-file -p 1.1
+'
+
+test_expect_success 'show object from second pack' '
+ git cat-file -p 2.2
+'
+
+for reuse in false single multi
+do
+ test_expect_success "full clone (pack.allowPackReuse=$reuse)" '
+ rm -fr clone.git &&
+
+ git config pack.allowPackReuse $reuse &&
+ git clone --no-local --bare . clone.git
+ '
+done
+
+test_expect_success 'relink existing MIDX layer' '
+ rm -fr "$midxdir" &&
+
+ GIT_TEST_MIDX_WRITE_REV=1 git multi-pack-index write --bitmap &&
+
+ midx_hash="$(test-tool read-midx --checksum $objdir)" &&
+
+ test_path_is_file "$packdir/multi-pack-index" &&
+ test_path_is_file "$packdir/multi-pack-index-$midx_hash.bitmap" &&
+ test_path_is_file "$packdir/multi-pack-index-$midx_hash.rev" &&
+
+ test_commit another &&
+ git repack -d &&
+ git multi-pack-index write --bitmap --incremental &&
+
+ test_path_is_missing "$packdir/multi-pack-index" &&
+ test_path_is_missing "$packdir/multi-pack-index-$midx_hash.bitmap" &&
+ test_path_is_missing "$packdir/multi-pack-index-$midx_hash.rev" &&
+
+ test_path_is_file "$midxdir/multi-pack-index-$midx_hash.midx" &&
+ test_path_is_file "$midxdir/multi-pack-index-$midx_hash.bitmap" &&
+ test_path_is_file "$midxdir/multi-pack-index-$midx_hash.rev" &&
+ test_line_count = 2 "$midx_chain"
+
+'
+
test_done
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 3f81f16e13..8f018d2f23 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -55,6 +55,13 @@ test_expect_success setup '
echo Rebase &&
git log'
+test_expect_success 'send-pack does not crash with -h' '
+ test_expect_code 129 git send-pack -h >usage &&
+ test_grep "[Uu]sage: git send-pack " usage &&
+ test_expect_code 129 nongit git send-pack -h >usage &&
+ test_grep "[Uu]sage: git send-pack " usage
+'
+
test_expect_success 'pack the source repository' '
git repack -a -d &&
git prune
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
index 195fc64dd4..845ca43ea0 100755
--- a/t/t5503-tagfollow.sh
+++ b/t/t5503-tagfollow.sh
@@ -160,4 +160,18 @@ test_expect_success 'new clone fetch main and tags' '
test_cmp expect actual
'
+test_expect_success 'fetch specific OID with tag following' '
+ git init --bare clone3.git &&
+ (
+ cd clone3.git &&
+ git remote add origin .. &&
+ git fetch origin $B:refs/heads/main &&
+
+ git -C .. for-each-ref >expect &&
+ git for-each-ref >actual &&
+
+ test_cmp expect actual
+ )
+'
+
test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index bb7e0c6879..82fccf8e36 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -1123,7 +1123,7 @@ Pull: refs/heads/main:refs/heads/origin
Pull: refs/heads/next:refs/heads/origin2
EOF
-test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/remotes' '
+test_expect_success !WITH_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/remotes' '
git clone one five &&
origin_url=$(pwd)/one &&
(
@@ -1149,7 +1149,7 @@ test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file i
)
'
-test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/branches' '
+test_expect_success !WITH_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/branches' '
git clone --template= one six &&
origin_url=$(pwd)/one &&
(
@@ -1165,7 +1165,7 @@ test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file i
)
'
-test_expect_success WITHOUT_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/branches (2)' '
+test_expect_success !WITH_BREAKING_CHANGES 'migrate a remote from named file in $GIT_DIR/branches (2)' '
git clone --template= one seven &&
(
cd seven &&
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 4e6026c611..8ac04d742c 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -104,7 +104,7 @@ test_expect_success setup '
git config remote.config-glob.fetch refs/heads/*:refs/remotes/rem/* &&
remotes="$remotes config-glob" &&
- if test_have_prereq WITHOUT_BREAKING_CHANGES
+ if ! test_have_prereq WITH_BREAKING_CHANGES
then
mkdir -p .git/remotes &&
cat >.git/remotes/remote-explicit <<-\EOF &&
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 85ed049627..dabcc5f811 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -495,7 +495,7 @@ test_expect_success 'push tag with non-existent, incomplete dest' '
'
-test_expect_success 'push sha1 with non-existent, incomplete dest' '
+test_expect_success 'push oid with non-existent, incomplete dest' '
mk_test testrepo &&
test_must_fail git push testrepo $(git rev-parse main):foo
@@ -975,7 +975,7 @@ test_expect_success 'allow push to HEAD of non-bare repository (config)' '
! grep "warning: updating the current branch" stderr
'
-test_expect_success WITHOUT_BREAKING_CHANGES 'fetch with branches' '
+test_expect_success !WITH_BREAKING_CHANGES 'fetch with branches' '
mk_empty testrepo &&
git branch second $the_first_commit &&
git checkout second &&
@@ -991,7 +991,7 @@ test_expect_success WITHOUT_BREAKING_CHANGES 'fetch with branches' '
git checkout main
'
-test_expect_success WITHOUT_BREAKING_CHANGES 'fetch with branches containing #' '
+test_expect_success !WITH_BREAKING_CHANGES 'fetch with branches containing #' '
mk_empty testrepo &&
mkdir testrepo/.git/branches &&
echo "..#second" > testrepo/.git/branches/branch2 &&
@@ -1005,7 +1005,7 @@ test_expect_success WITHOUT_BREAKING_CHANGES 'fetch with branches containing #'
git checkout main
'
-test_expect_success WITHOUT_BREAKING_CHANGES 'push with branches' '
+test_expect_success !WITH_BREAKING_CHANGES 'push with branches' '
mk_empty testrepo &&
git checkout second &&
@@ -1022,7 +1022,7 @@ test_expect_success WITHOUT_BREAKING_CHANGES 'push with branches' '
)
'
-test_expect_success WITHOUT_BREAKING_CHANGES 'push with branches containing #' '
+test_expect_success !WITH_BREAKING_CHANGES 'push with branches containing #' '
mk_empty testrepo &&
test_when_finished "rm -rf .git/branches" &&
@@ -1251,7 +1251,7 @@ do
'
done
-test_expect_success 'fetch exact SHA1' '
+test_expect_success 'fetch exact oid' '
mk_test testrepo heads/main hidden/one &&
git push testrepo main:refs/hidden/one &&
(
@@ -1297,7 +1297,7 @@ test_expect_success 'fetch exact SHA1' '
)
'
-test_expect_success 'fetch exact SHA1 in protocol v2' '
+test_expect_success 'fetch exact oid in protocol v2' '
mk_test testrepo heads/main hidden/one &&
git push testrepo main:refs/hidden/one &&
git -C testrepo config transfer.hiderefs refs/hidden &&
@@ -1312,8 +1312,10 @@ test_expect_success 'fetch exact SHA1 in protocol v2' '
test_must_fail git -C child cat-file -t $the_commit &&
# fetching the hidden object succeeds by default
- # NEEDSWORK: should this match the v0 behavior instead?
- git -C child fetch -v ../testrepo $the_commit:refs/heads/copy
+ GIT_TRACE_PACKET=$PWD/trace.out \
+ git -C child fetch -v ../testrepo $the_commit:refs/heads/copy &&
+
+ test_grep ! "ref-prefix.*$the_commit" trace.out
'
for configallowtipsha1inwant in true false
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index d3df81e785..4d0cbe9872 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -665,7 +665,7 @@ test_expect_success 'even with handcrafted request, filter does not work if not
test-tool -C server serve-v2 --stateless-rpc <in >/dev/null
'
-test_expect_success 'default refspec is used to filter ref when fetchcing' '
+test_expect_success 'default refspec is used to filter ref when fetching' '
test_when_finished "rm -f log" &&
GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \
@@ -679,6 +679,48 @@ test_expect_success 'default refspec is used to filter ref when fetchcing' '
grep "ref-prefix refs/tags/" log
'
+test_expect_success 'set up parent for prefix tests' '
+ git init prefix-parent &&
+ git -C prefix-parent commit --allow-empty -m foo &&
+ git -C prefix-parent tag my-tag &&
+ git -C prefix-parent branch unrelated-branch
+'
+
+test_expect_success 'empty refspec filters refs when fetching' '
+ git init configless-child &&
+
+ test_when_finished "rm -f log" &&
+ GIT_TRACE_PACKET="$(pwd)/log" \
+ git -C configless-child fetch ../prefix-parent &&
+ test_grep ! unrelated-branch log
+'
+
+test_expect_success 'exact oid fetch with tag following' '
+ git init exact-oid-tags &&
+
+ commit=$(git -C prefix-parent rev-parse --verify HEAD) &&
+
+ test_when_finished "rm -f log" &&
+ GIT_TRACE_PACKET="$(pwd)/log" \
+ git -C exact-oid-tags fetch ../prefix-parent \
+ $commit:refs/heads/exact &&
+ test_grep ! unrelated-branch log &&
+ git -C exact-oid-tags rev-parse --verify my-tag
+'
+
+test_expect_success 'exact oid fetch avoids pointless HEAD request' '
+ git init exact-oid-head &&
+ git -C exact-oid-head remote add origin ../prefix-parent &&
+
+ commit=$(git -C prefix-parent rev-parse --verify HEAD) &&
+
+ test_when_finished "rm -f log" &&
+ GIT_TRACE_PACKET="$(pwd)/log" \
+ git -C exact-oid-head fetch --no-tags origin \
+ $commit:refs/heads/exact &&
+ test_grep ! command=ls-refs log
+'
+
test_expect_success 'fetch supports various ways of have lines' '
rm -rf server client trace &&
git init server &&
diff --git a/t/t5710-promisor-remote-capability.sh b/t/t5710-promisor-remote-capability.sh
new file mode 100755
index 0000000000..b35b774235
--- /dev/null
+++ b/t/t5710-promisor-remote-capability.sh
@@ -0,0 +1,373 @@
+#!/bin/sh
+
+test_description='handling of promisor remote advertisement'
+
+. ./test-lib.sh
+
+GIT_TEST_MULTI_PACK_INDEX=0
+GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL=0
+
+# Setup the repository with three commits, this way HEAD is always
+# available and we can hide commit 1 or 2.
+test_expect_success 'setup: create "template" repository' '
+ git init template &&
+ test_commit -C template 1 &&
+ test_commit -C template 2 &&
+ test_commit -C template 3 &&
+ test-tool genrandom foo 10240 >template/foo &&
+ git -C template add foo &&
+ git -C template commit -m foo
+'
+
+# A bare repo will act as a server repo with unpacked objects.
+test_expect_success 'setup: create bare "server" repository' '
+ git clone --bare --no-local template server &&
+ mv server/objects/pack/pack-* . &&
+ packfile=$(ls pack-*.pack) &&
+ git -C server unpack-objects --strict <"$packfile"
+'
+
+check_missing_objects () {
+ git -C "$1" rev-list --objects --all --missing=print > all.txt &&
+ perl -ne 'print if s/^[?]//' all.txt >missing.txt &&
+ test_line_count = "$2" missing.txt &&
+ if test "$2" -lt 2
+ then
+ test "$3" = "$(cat missing.txt)"
+ else
+ test -f "$3" &&
+ sort <"$3" >expected_sorted &&
+ sort <missing.txt >actual_sorted &&
+ test_cmp expected_sorted actual_sorted
+ fi
+}
+
+initialize_server () {
+ count="$1"
+ missing_oids="$2"
+
+ # Repack everything first
+ git -C server -c repack.writebitmaps=false repack -a -d &&
+
+ # Remove promisor file in case they exist, useful when reinitializing
+ rm -rf server/objects/pack/*.promisor &&
+
+ # Repack without the largest object and create a promisor pack on server
+ git -C server -c repack.writebitmaps=false repack -a -d \
+ --filter=blob:limit=5k --filter-to="$(pwd)/pack" &&
+ promisor_file=$(ls server/objects/pack/*.pack | sed "s/\.pack/.promisor/") &&
+ >"$promisor_file" &&
+
+ # Check objects missing on the server
+ check_missing_objects server "$count" "$missing_oids"
+}
+
+copy_to_lop () {
+ oid_path="$(test_oid_to_path $1)" &&
+ path="server/objects/$oid_path" &&
+ path2="lop/objects/$oid_path" &&
+ mkdir -p $(dirname "$path2") &&
+ cp "$path" "$path2"
+}
+
+test_expect_success "setup for testing promisor remote advertisement" '
+ # Create another bare repo called "lop" (for Large Object Promisor)
+ git init --bare lop &&
+
+ # Copy the largest object from server to lop
+ obj="HEAD:foo" &&
+ oid="$(git -C server rev-parse $obj)" &&
+ copy_to_lop "$oid" &&
+
+ initialize_server 1 "$oid" &&
+
+ # Configure lop as promisor remote for server
+ git -C server remote add lop "file://$(pwd)/lop" &&
+ git -C server config remote.lop.promisor true &&
+
+ git -C lop config uploadpack.allowFilter true &&
+ git -C lop config uploadpack.allowAnySHA1InWant true &&
+ git -C server config uploadpack.allowFilter true &&
+ git -C server config uploadpack.allowAnySHA1InWant true
+'
+
+test_expect_success "clone with promisor.advertise set to 'true'" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ # Clone from server to create a client
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=All \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "clone with promisor.advertise set to 'false'" '
+ git -C server config promisor.advertise false &&
+ test_when_finished "rm -rf client" &&
+
+ # Clone from server to create a client
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=All \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is not missing on the server
+ check_missing_objects server 0 "" &&
+
+ # Reinitialize server so that the largest object is missing again
+ initialize_server 1 "$oid"
+'
+
+test_expect_success "clone with promisor.acceptfromserver set to 'None'" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ # Clone from server to create a client
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=None \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is not missing on the server
+ check_missing_objects server 0 "" &&
+
+ # Reinitialize server so that the largest object is missing again
+ initialize_server 1 "$oid"
+'
+
+test_expect_success "init + fetch with promisor.advertise set to 'true'" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ mkdir client &&
+ git -C client init &&
+ git -C client config remote.lop.promisor true &&
+ git -C client config remote.lop.fetch "+refs/heads/*:refs/remotes/lop/*" &&
+ git -C client config remote.lop.url "file://$(pwd)/lop" &&
+ git -C client config remote.server.url "file://$(pwd)/server" &&
+ git -C client config remote.server.fetch "+refs/heads/*:refs/remotes/server/*" &&
+ git -C client config promisor.acceptfromserver All &&
+ GIT_NO_LAZY_FETCH=0 git -C client fetch --filter="blob:limit=5k" server &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "clone with promisor.acceptfromserver set to 'KnownName'" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ # Clone from server to create a client
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=KnownName \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "clone with 'KnownName' and different remote names" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ # Clone from server to create a client
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.serverTwo.promisor=true \
+ -c remote.serverTwo.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.serverTwo.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=KnownName \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is not missing on the server
+ check_missing_objects server 0 "" &&
+
+ # Reinitialize server so that the largest object is missing again
+ initialize_server 1 "$oid"
+'
+
+test_expect_success "clone with 'KnownName' and missing URL in the config" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ # Clone from server to create a client
+ # Lazy fetching by the client from the LOP will fail because of the
+ # missing URL in the client config, so the server will have to lazy
+ # fetch from the LOP.
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c promisor.acceptfromserver=KnownName \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is not missing on the server
+ check_missing_objects server 0 "" &&
+
+ # Reinitialize server so that the largest object is missing again
+ initialize_server 1 "$oid"
+'
+
+test_expect_success "clone with promisor.acceptfromserver set to 'KnownUrl'" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ # Clone from server to create a client
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=KnownUrl \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "clone with 'KnownUrl' and different remote urls" '
+ ln -s lop serverTwo &&
+
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ # Clone from server to create a client
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/serverTwo" \
+ -c promisor.acceptfromserver=KnownUrl \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is not missing on the server
+ check_missing_objects server 0 "" &&
+
+ # Reinitialize server so that the largest object is missing again
+ initialize_server 1 "$oid"
+'
+
+test_expect_success "clone with 'KnownUrl' and url not configured on the server" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ test_when_finished "git -C server config set remote.lop.url \"file://$(pwd)/lop\"" &&
+ git -C server config unset remote.lop.url &&
+
+ # Clone from server to create a client
+ # It should fail because the client will reject the LOP as URLs are
+ # different, and the server cannot lazy fetch as the LOP URL is
+ # missing, so the remote name will be used instead which will fail.
+ test_must_fail env GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=KnownUrl \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "clone with 'KnownUrl' and empty url, so not advertised" '
+ git -C server config promisor.advertise true &&
+ test_when_finished "rm -rf client" &&
+
+ test_when_finished "git -C server config set remote.lop.url \"file://$(pwd)/lop\"" &&
+ git -C server config set remote.lop.url "" &&
+
+ # Clone from server to create a client
+ # It should fail because the client will reject the LOP as an empty URL is
+ # not advertised, and the server cannot lazy fetch as the LOP URL is empty,
+ # so the remote name will be used instead which will fail.
+ test_must_fail env GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=KnownUrl \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "clone with promisor.advertise set to 'true' but don't delete the client" '
+ git -C server config promisor.advertise true &&
+
+ # Clone from server to create a client
+ GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
+ -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
+ -c remote.lop.url="file://$(pwd)/lop" \
+ -c promisor.acceptfromserver=All \
+ --no-local --filter="blob:limit=5k" server client &&
+
+ # Check that the largest object is still missing on the server
+ check_missing_objects server 1 "$oid"
+'
+
+test_expect_success "setup for subsequent fetches" '
+ # Generate new commit with large blob
+ test-tool genrandom bar 10240 >template/bar &&
+ git -C template add bar &&
+ git -C template commit -m bar &&
+
+ # Fetch new commit with large blob
+ git -C server fetch origin &&
+ git -C server update-ref HEAD FETCH_HEAD &&
+ git -C server rev-parse HEAD >expected_head &&
+
+ # Repack everything twice and remove .promisor files before
+ # each repack. This makes sure everything gets repacked
+ # into a single packfile. The second repack is necessary
+ # because the first one fetches from lop and creates a new
+ # packfile and its associated .promisor file.
+
+ rm -f server/objects/pack/*.promisor &&
+ git -C server -c repack.writebitmaps=false repack -a -d &&
+ rm -f server/objects/pack/*.promisor &&
+ git -C server -c repack.writebitmaps=false repack -a -d &&
+
+ # Unpack everything
+ rm pack-* &&
+ mv server/objects/pack/pack-* . &&
+ packfile=$(ls pack-*.pack) &&
+ git -C server unpack-objects --strict <"$packfile" &&
+
+ # Copy new large object to lop
+ obj_bar="HEAD:bar" &&
+ oid_bar="$(git -C server rev-parse $obj_bar)" &&
+ copy_to_lop "$oid_bar" &&
+
+ # Reinitialize server so that the 2 largest objects are missing
+ printf "%s\n" "$oid" "$oid_bar" >expected_missing.txt &&
+ initialize_server 2 expected_missing.txt &&
+
+ # Create one more client
+ cp -r client client2
+'
+
+test_expect_success "subsequent fetch from a client when promisor.advertise is true" '
+ git -C server config promisor.advertise true &&
+
+ GIT_NO_LAZY_FETCH=0 git -C client pull origin &&
+
+ git -C client rev-parse HEAD >actual &&
+ test_cmp expected_head actual &&
+
+ cat client/bar >/dev/null &&
+
+ check_missing_objects server 2 expected_missing.txt
+'
+
+test_expect_success "subsequent fetch from a client when promisor.advertise is false" '
+ git -C server config promisor.advertise false &&
+
+ GIT_NO_LAZY_FETCH=0 git -C client2 pull origin &&
+
+ git -C client2 rev-parse HEAD >actual &&
+ test_cmp expected_head actual &&
+
+ cat client2/bar >/dev/null &&
+
+ check_missing_objects server 1 "$oid"
+'
+
+test_done
diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh
index de1e87f162..4cecb6224c 100755
--- a/t/t6012-rev-list-simplify.sh
+++ b/t/t6012-rev-list-simplify.sh
@@ -177,7 +177,7 @@ test_expect_success '--full-diff is not affected by --parents' '
# \ / /\ /
# `---X--' `---Y--'
#
-# This example is explained in Documentation/rev-list-options.txt
+# This example is explained in Documentation/rev-list-options.adoc
test_expect_success 'setup rebuild repo' '
rm -rf .git * &&
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 76843a6169..256ccaefb7 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -292,15 +292,23 @@ test_expect_success 'name-rev --annotate-stdin' '
echo "$rev ($name)" >>expect.unsorted || return 1
done &&
sort <expect.unsorted >expect &&
- git rev-list --all | git name-rev --annotate-stdin >actual.unsorted &&
+ git rev-list --all >list &&
+ git name-rev --annotate-stdin <list >actual.unsorted &&
sort <actual.unsorted >actual &&
test_cmp expect actual
'
-test_expect_success 'name-rev --stdin deprecated' "
- git rev-list --all | git name-rev --stdin 2>actual &&
- grep -E 'warning: --stdin is deprecated' actual
-"
+test_expect_success 'name-rev --stdin deprecated' '
+ git rev-list --all >list &&
+ if ! test_have_prereq WITH_BREAKING_CHANGES
+ then
+ git name-rev --stdin <list 2>actual &&
+ test_grep "warning: --stdin is deprecated" actual
+ else
+ test_must_fail git name-rev --stdin <list 2>actual &&
+ test_grep "unknown option .stdin." actual
+ fi
+'
test_expect_success 'describe --contains with the exact tags' '
echo "A^0" >expect &&
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
index a5c7794385..9b4f4306c4 100755
--- a/t/t6300-for-each-ref.sh
+++ b/t/t6300-for-each-ref.sh
@@ -292,6 +292,13 @@ test_expect_success 'Check invalid atoms names are errors' '
test_must_fail git for-each-ref --format="%(INVALID)" refs/heads
'
+test_expect_success 'for-each-ref does not crash with -h' '
+ test_expect_code 129 git for-each-ref -h >usage &&
+ test_grep "[Uu]sage: git for-each-ref " usage &&
+ test_expect_code 129 nongit git for-each-ref -h >usage &&
+ test_grep "[Uu]sage: git for-each-ref " usage
+'
+
test_expect_success 'Check format specifiers are ignored in naming date atoms' '
git for-each-ref --format="%(authordate)" refs/heads &&
git for-each-ref --format="%(authordate:default) %(authordate)" refs/heads &&
diff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh
index 94080c65d1..e0785410cd 100755
--- a/t/t6423-merge-rename-directories.sh
+++ b/t/t6423-merge-rename-directories.sh
@@ -5363,6 +5363,47 @@ test_expect_merge_algorithm failure success '12m: Change parent of renamed-dir t
)
'
+test_setup_12n () {
+ git init 12n &&
+ (
+ cd 12n &&
+
+ mkdir tools &&
+ echo hello >tools/hello &&
+ git add tools/hello &&
+ git commit -m "O" &&
+
+ git branch O &&
+ git branch A &&
+ git branch B &&
+
+ git switch A &&
+ echo world >world &&
+ git add world &&
+ git commit -q world -m 'Add world' &&
+
+ git mv world tools/world &&
+ git commit -m "Move world into tools/" &&
+
+ git switch B &&
+ git mv tools/hello hello &&
+ git commit -m "Move hello from tools/ to toplevel"
+ )
+}
+
+test_expect_success '12n: Directory rename transitively makes rename back to self' '
+ test_setup_12n &&
+ (
+ cd 12n &&
+
+ git checkout -q B^0 &&
+
+ test_must_fail git cherry-pick A^0 >out &&
+ grep "CONFLICT (file location).*should perhaps be moved" out
+ )
+'
+
+
###########################################################################
# SECTION 13: Checking informational and conflict messages
#
@@ -5549,9 +5590,9 @@ test_expect_success '13b(info): messages for transitive rename with conflicted c
# Commit A: y/{b,c,d}, x/e
# Commit B: z/{b,c,d}, x/e
# Expected: y/{b,c,d}, x/e, with info or conflict messages for d
-# A: renamed x/d -> z/d; B: renamed z/ -> y/ AND renamed x/d to y/d
-# One could argue A had partial knowledge of what was done with
-# d and B had full knowledge, but that's a slippery slope as
+# B: renamed x/d -> z/d; A: renamed z/ -> y/ AND renamed x/d to y/d
+# One could argue B had partial knowledge of what was done with
+# d and A had full knowledge, but that's a slippery slope as
# shown in testcase 13d.
test_setup_13c () {
diff --git a/t/t6427-diff3-conflict-markers.sh b/t/t6427-diff3-conflict-markers.sh
index dd5fe6a402..57569c4f4b 100755
--- a/t/t6427-diff3-conflict-markers.sh
+++ b/t/t6427-diff3-conflict-markers.sh
@@ -207,7 +207,7 @@ test_expect_success 'rebase --apply describes fake ancestor base' '
cd rebase &&
git rebase --abort &&
test_must_fail git -c merge.conflictstyle=diff3 rebase --apply main &&
- grep "||||||| constructed merge base" file
+ grep "||||||| constructed fake ancestor" file
)
'
diff --git a/t/t6434-merge-recursive-rename-options.sh b/t/t6434-merge-recursive-rename-options.sh
index a11707835b..6e913c30a1 100755
--- a/t/t6434-merge-recursive-rename-options.sh
+++ b/t/t6434-merge-recursive-rename-options.sh
@@ -22,7 +22,7 @@ R075 2-old 2-new
R100 3-old 3-new
Actual similarity indices are parsed from diff output. We rely on the fact that
-they are rounded down (see, e.g., Documentation/diff-generate-patch.txt, which
+they are rounded down (see, e.g., Documentation/diff-generate-patch.adoc, which
mentions this in a different context).
'
diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh
index 6f526c37c2..2c147072c1 100755
--- a/t/t7030-verify-tag.sh
+++ b/t/t7030-verify-tag.sh
@@ -7,6 +7,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-gpg.sh"
+test_expect_success GPG 'verify-tag does not crash with -h' '
+ test_expect_code 129 git verify-tag -h >usage &&
+ test_grep "[Uu]sage: git verify-tag " usage &&
+ test_expect_code 129 nongit git verify-tag -h >usage &&
+ test_grep "[Uu]sage: git verify-tag " usage
+'
+
test_expect_success GPG 'create signed tags' '
echo 1 >file && git add file &&
test_tick && git commit -m initial &&
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
index 0d2dd29fe6..39677e859a 100755
--- a/t/t7510-signed-commit.sh
+++ b/t/t7510-signed-commit.sh
@@ -8,6 +8,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
GNUPGHOME_NOT_USED=$GNUPGHOME
. "$TEST_DIRECTORY/lib-gpg.sh"
+test_expect_success GPG 'verify-commit does not crash with -h' '
+ test_expect_code 129 git verify-commit -h >usage &&
+ test_grep "[Uu]sage: git verify-commit " usage &&
+ test_expect_code 129 nongit git verify-commit -h >usage &&
+ test_grep "[Uu]sage: git verify-commit " usage
+'
+
test_expect_success GPG 'create signed commits' '
test_oid_cache <<-\EOF &&
header sha1:gpgsig
diff --git a/t/t7615-diff-algo-with-mergy-operations.sh b/t/t7615-diff-algo-with-mergy-operations.sh
index 3b1aad0167..ac5863e788 100755
--- a/t/t7615-diff-algo-with-mergy-operations.sh
+++ b/t/t7615-diff-algo-with-mergy-operations.sh
@@ -26,7 +26,7 @@ GIT_TEST_MERGE_ALGORITHM=recursive
test_expect_success 'merge c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' '
git reset --hard c1 &&
- test_must_fail git merge -s recursive c2
+ test_must_fail git merge -s recursive -Xdiff-algorithm=myers c2
'
test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with -Xdiff-algorithm=histogram' '
@@ -42,7 +42,7 @@ test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with
test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' '
git reset --hard c1 &&
- test_must_fail git cherry-pick -s recursive c2
+ test_must_fail git cherry-pick -s recursive -Xdiff-algorithm=myers c2
'
test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy succeeds with -Xdiff-algorithm=histogram' '
diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh
index 959e6e2648..8aebfb45f5 100755
--- a/t/t7704-repack-cruft.sh
+++ b/t/t7704-repack-cruft.sh
@@ -149,7 +149,7 @@ generate_cruft_pack () {
echo "$packdir/pack-$pack.mtimes"
}
-test_expect_success '--max-cruft-size creates new packs when above threshold' '
+test_expect_success '--max-cruft-size creates new packs when too large' '
git init max-cruft-size-large &&
(
cd max-cruft-size-large &&
@@ -173,7 +173,7 @@ test_expect_success '--max-cruft-size creates new packs when above threshold' '
)
'
-test_expect_success '--max-cruft-size combines existing packs when below threshold' '
+test_expect_success '--max-cruft-size combines existing packs when not too large' '
git init max-cruft-size-small &&
(
cd max-cruft-size-small &&
@@ -194,10 +194,13 @@ test_expect_success '--max-cruft-size combines existing packs when below thresho
)
'
-test_expect_success '--max-cruft-size combines smaller packs first' '
- git init max-cruft-size-consume-small &&
+test_expect_success '--combine-cruft-below-size combines packs' '
+ repo=combine-cruft-below-size &&
+ test_when_finished "rm -fr $repo" &&
+
+ git init "$repo" &&
(
- cd max-cruft-size-consume-small &&
+ cd "$repo" &&
test_commit base &&
git repack -ad &&
@@ -211,11 +214,11 @@ test_expect_success '--max-cruft-size combines smaller packs first' '
test-tool pack-mtimes "$(basename $cruft_bar)" >>expect.raw &&
sort expect.raw >expect.objects &&
- # repacking with `--max-cruft-size=2M` should combine
- # both 0.5 MiB packs together, instead of, say, one of
- # the 0.5 MiB packs with the 1.0 MiB pack
+ # Repacking with `--combine-cruft-below-size=1M`
+ # should combine both 0.5 MiB packs together, but
+ # ignore the two packs which are >= 1.0 MiB.
ls $packdir/pack-*.mtimes | sort >cruft.before &&
- git repack -d --cruft --max-cruft-size=2M &&
+ git repack -d --cruft --combine-cruft-below-size=1M &&
ls $packdir/pack-*.mtimes | sort >cruft.after &&
comm -13 cruft.before cruft.after >cruft.new &&
@@ -224,11 +227,12 @@ test_expect_success '--max-cruft-size combines smaller packs first' '
test_line_count = 1 cruft.new &&
test_line_count = 2 cruft.removed &&
- # the two smaller packs should be rolled up first
+ # The two packs smaller than 1.0MiB should be repacked
+ # together.
printf "%s\n" $cruft_foo $cruft_bar | sort >expect.removed &&
test_cmp expect.removed cruft.removed &&
- # ...and contain the set of objects rolled up
+ # ...and contain the set of objects rolled up.
test-tool pack-mtimes "$(basename $(cat cruft.new))" >actual.raw &&
sort actual.raw >actual.objects &&
@@ -236,10 +240,10 @@ test_expect_success '--max-cruft-size combines smaller packs first' '
)
'
-test_expect_success 'setup --max-cruft-size with freshened objects' '
- git init max-cruft-size-freshen &&
+test_expect_success 'setup cruft with freshened objects' '
+ git init cruft-freshen &&
(
- cd max-cruft-size-freshen &&
+ cd cruft-freshen &&
test_commit base &&
git repack -ad &&
@@ -257,9 +261,9 @@ test_expect_success 'setup --max-cruft-size with freshened objects' '
)
'
-test_expect_success '--max-cruft-size with freshened objects (loose)' '
+test_expect_success 'cruft with freshened objects (loose)' '
(
- cd max-cruft-size-freshen &&
+ cd cruft-freshen &&
# regenerate the object, setting its mtime to be more recent
foo="$(generate_random_blob foo 64)" &&
@@ -275,9 +279,9 @@ test_expect_success '--max-cruft-size with freshened objects (loose)' '
)
'
-test_expect_success '--max-cruft-size with freshened objects (packed)' '
+test_expect_success 'cruft with freshened objects (packed)' '
(
- cd max-cruft-size-freshen &&
+ cd cruft-freshen &&
# regenerate the object and store it in a packfile,
# setting its mtime to be more recent
@@ -304,6 +308,70 @@ test_expect_success '--max-cruft-size with freshened objects (packed)' '
)
'
+test_expect_success 'multi-cruft with freshened objects (previously cruft)' '
+ repo="max-cruft-size-threshold" &&
+
+ test_when_finished "rm -fr $repo" &&
+ git init "$repo" &&
+ (
+ cd "$repo" &&
+
+ test_commit base &&
+ foo="$(generate_random_blob foo $((2*1024*1024)))" &&
+ bar="$(generate_random_blob bar $((2*1024*1024)))" &&
+ baz="$(generate_random_blob baz $((2*1024*1024)))" &&
+
+ test-tool chmtime --get -100000 \
+ "$objdir/$(test_oid_to_path "$foo")" >foo.old &&
+ test-tool chmtime --get -100000 \
+ "$objdir/$(test_oid_to_path "$bar")" >bar.old &&
+ test-tool chmtime --get -100000 \
+ "$objdir/$(test_oid_to_path "$baz")" >baz.old &&
+
+ git repack --cruft -d &&
+
+ # Make an identical copy of foo stored in a pack with a more
+ # recent mtime.
+ foo="$(generate_random_blob foo $((2*1024*1024)))" &&
+ foo_pack="$(echo "$foo" | git pack-objects $packdir/pack)" &&
+ test-tool chmtime --get -100 \
+ "$packdir/pack-$foo_pack.pack" >foo.new &&
+ git prune-packed &&
+
+ # Make a loose copy of bar, also with a more recent mtime.
+ bar="$(generate_random_blob bar $((2*1024*1024)))" &&
+ test-tool chmtime --get -100 \
+ "$objdir/$(test_oid_to_path "$bar")" >bar.new &&
+
+ # Make a new cruft object $quux to ensure we do not
+ # generate an identical pack to the existing cruft
+ # pack.
+ quux="$(generate_random_blob quux $((1024)))" &&
+ test-tool chmtime --get -100 \
+ "$objdir/$(test_oid_to_path "$quux")" >quux.new &&
+
+ git repack --cruft --max-cruft-size=3M -d &&
+
+ for p in $packdir/pack-*.mtimes
+ do
+ test-tool pack-mtimes "$(basename "$p")" || return 1
+ done >actual.raw &&
+ sort actual.raw >actual &&
+
+ # Among the set of all cruft packs, we should see the
+ # new mtimes for object $foo and $bar, as well as the
+ # single new copy of $baz.
+ sort >expect <<-EOF &&
+ $foo $(cat foo.new)
+ $bar $(cat bar.new)
+ $baz $(cat baz.old)
+ $quux $(cat quux.new)
+ EOF
+
+ test_cmp expect actual
+ )
+'
+
test_expect_success '--max-cruft-size with pruning' '
git init max-cruft-size-prune &&
(
@@ -411,4 +479,249 @@ test_expect_success 'reachable packs are preferred over cruft ones' '
)
'
+test_expect_success 'repack --cruft generates a cruft pack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git branch -M main &&
+ git checkout --orphan other &&
+ test_commit unreachable &&
+
+ git checkout main &&
+ git branch -D other &&
+ git tag -d unreachable &&
+ # objects are not cruft if they are contained in the reflogs
+ git reflog expire --all --expire=all &&
+
+ git rev-list --objects --all --no-object-names >reachable.raw &&
+ git cat-file --batch-all-objects --batch-check="%(objectname)" >objects &&
+ sort <reachable.raw >reachable &&
+ comm -13 reachable objects >unreachable &&
+
+ git repack --cruft -d &&
+
+ cruft=$(basename $(ls $packdir/pack-*.mtimes) .mtimes) &&
+ pack=$(basename $(ls $packdir/pack-*.pack | grep -v $cruft) .pack) &&
+
+ git show-index <$packdir/$pack.idx >actual.raw &&
+ cut -f2 -d" " actual.raw | sort >actual &&
+ test_cmp reachable actual &&
+
+ git show-index <$packdir/$cruft.idx >actual.raw &&
+ cut -f2 -d" " actual.raw | sort >actual &&
+ test_cmp unreachable actual
+ )
+'
+
+test_expect_success 'cruft packs are not included in geometric repack' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git repack -Ad &&
+ git branch -M main &&
+
+ git checkout --orphan other &&
+ test_commit cruft &&
+ git repack -d &&
+
+ git checkout main &&
+ git branch -D other &&
+ git tag -d cruft &&
+ git reflog expire --all --expire=all &&
+
+ git repack --cruft &&
+
+ find $packdir -type f | sort >before &&
+ git repack --geometric=2 -d &&
+ find $packdir -type f | sort >after &&
+
+ test_cmp before after
+ )
+'
+
+test_expect_success 'repack --geometric collects once-cruft objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit reachable &&
+ git repack -Ad &&
+ git branch -M main &&
+
+ git checkout --orphan other &&
+ git rm -rf . &&
+ test_commit --no-tag cruft &&
+ cruft="$(git rev-parse HEAD)" &&
+
+ git checkout main &&
+ git branch -D other &&
+ git reflog expire --all --expire=all &&
+
+ # Pack the objects created in the previous step into a cruft
+ # pack. Intentionally leave loose copies of those objects
+ # around so we can pick them up in a subsequent --geometric
+ # reapack.
+ git repack --cruft &&
+
+ # Now make those objects reachable, and ensure that they are
+ # packed into the new pack created via a --geometric repack.
+ git update-ref refs/heads/other $cruft &&
+
+ # Without this object, the set of unpacked objects is exactly
+ # the set of objects already in the cruft pack. Tweak that set
+ # to ensure we do not overwrite the cruft pack entirely.
+ test_commit reachable2 &&
+
+ find $packdir -name "pack-*.idx" | sort >before &&
+ git repack --geometric=2 -d &&
+ find $packdir -name "pack-*.idx" | sort >after &&
+
+ {
+ git rev-list --objects --no-object-names $cruft &&
+ git rev-list --objects --no-object-names reachable..reachable2
+ } >want.raw &&
+ sort want.raw >want &&
+
+ pack=$(comm -13 before after) &&
+ git show-index <$pack >objects.raw &&
+
+ cut -d" " -f2 objects.raw | sort >got &&
+
+ test_cmp want got
+ )
+'
+
+test_expect_success 'cruft repack with no reachable objects' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ git repack -ad &&
+
+ base="$(git rev-parse base)" &&
+
+ git for-each-ref --format="delete %(refname)" >in &&
+ git update-ref --stdin <in &&
+ git reflog expire --all --expire=all &&
+ rm -fr .git/index &&
+
+ git repack --cruft -d &&
+
+ git cat-file -t $base
+ )
+'
+
+find_pack () {
+ for idx in $(ls $packdir/pack-*.idx)
+ do
+ git show-index <$idx >out &&
+ if grep -q "$1" out
+ then
+ echo $idx
+ fi || return 1
+ done
+}
+
+test_expect_success 'cruft repack with --max-pack-size' '
+ git init max-pack-size &&
+ (
+ cd max-pack-size &&
+ test_commit base &&
+
+ # two cruft objects which exceed the maximum pack size
+ foo=$(generate_random_blob foo 1048576) &&
+ bar=$(generate_random_blob bar 1048576) &&
+ test-tool chmtime --get -1000 \
+ "$objdir/$(test_oid_to_path $foo)" >foo.mtime &&
+ test-tool chmtime --get -2000 \
+ "$objdir/$(test_oid_to_path $bar)" >bar.mtime &&
+ git repack --cruft --max-pack-size=1M &&
+ find $packdir -name "*.mtimes" >cruft &&
+ test_line_count = 2 cruft &&
+
+ foo_mtimes="$(basename $(find_pack $foo) .idx).mtimes" &&
+ bar_mtimes="$(basename $(find_pack $bar) .idx).mtimes" &&
+ test-tool pack-mtimes $foo_mtimes >foo.actual &&
+ test-tool pack-mtimes $bar_mtimes >bar.actual &&
+
+ echo "$foo $(cat foo.mtime)" >foo.expect &&
+ echo "$bar $(cat bar.mtime)" >bar.expect &&
+
+ test_cmp foo.expect foo.actual &&
+ test_cmp bar.expect bar.actual &&
+ test "$foo_mtimes" != "$bar_mtimes"
+ )
+'
+
+test_expect_success 'cruft repack with pack.packSizeLimit' '
+ (
+ cd max-pack-size &&
+ # repack everything back together to remove the existing cruft
+ # pack (but to keep its objects)
+ git repack -adk &&
+ git -c pack.packSizeLimit=1M repack --cruft &&
+ # ensure the same post condition is met when --max-pack-size
+ # would otherwise be inferred from the configuration
+ find $packdir -name "*.mtimes" >cruft &&
+ test_line_count = 2 cruft &&
+ for pack in $(cat cruft)
+ do
+ test-tool pack-mtimes "$(basename $pack)" >objects &&
+ test_line_count = 1 objects || return 1
+ done
+ )
+'
+
+test_expect_success 'cruft repack respects repack.cruftWindow' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+
+ GIT_TRACE2_EVENT=$(pwd)/event.trace \
+ git -c pack.window=1 -c repack.cruftWindow=2 repack \
+ --cruft --window=3 &&
+
+ grep "pack-objects.*--window=2.*--cruft" event.trace
+ )
+'
+
+test_expect_success 'cruft repack respects --window by default' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+
+ GIT_TRACE2_EVENT=$(pwd)/event.trace \
+ git -c pack.window=2 repack --cruft --window=3 &&
+
+ grep "pack-objects.*--window=3.*--cruft" event.trace
+ )
+'
+
+test_expect_success 'cruft repack respects --quiet' '
+ git init repo &&
+ test_when_finished "rm -fr repo" &&
+ (
+ cd repo &&
+
+ test_commit base &&
+ GIT_PROGRESS_DELAY=0 git repack --cruft --quiet 2>err &&
+ test_must_be_empty err
+ )
+'
+
test_done
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index 40427883ec..304bac5b1d 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -8,6 +8,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
test_expect_success 'setup' '
@@ -253,6 +254,24 @@ test_expect_success 'signed-tags=verbatim' '
'
+test_expect_success 'signed-tags=warn-verbatim' '
+
+ git fast-export --signed-tags=warn-verbatim sign-your-name >output 2>err &&
+ grep PGP output &&
+ test -s err
+
+'
+
+# 'warn' is a backward-compatibility alias for 'warn-verbatim'; test
+# that it keeps working.
+test_expect_success 'signed-tags=warn' '
+
+ git fast-export --signed-tags=warn sign-your-name >output 2>err &&
+ grep PGP output &&
+ test -s err
+
+'
+
test_expect_success 'signed-tags=strip' '
git fast-export --signed-tags=strip sign-your-name > output &&
@@ -266,10 +285,107 @@ test_expect_success 'signed-tags=warn-strip' '
test -s err
'
+test_expect_success GPG 'set up signed commit' '
+
+ # Generate a commit with both "gpgsig" and "encoding" set, so
+ # that we can test that fast-import gets the ordering correct
+ # between the two.
+ test_config i18n.commitEncoding ISO-8859-1 &&
+ git checkout -f -b commit-signing main &&
+ echo Sign your name >file-sign &&
+ git add file-sign &&
+ git commit -S -m "signed commit" &&
+ COMMIT_SIGNING=$(git rev-parse --verify commit-signing)
+
+'
+
+test_expect_success GPG 'signed-commits default' '
+
+ sane_unset FAST_EXPORT_SIGNED_COMMITS_NOABORT &&
+ test_must_fail git fast-export --reencode=no commit-signing &&
+
+ FAST_EXPORT_SIGNED_COMMITS_NOABORT=1 git fast-export --reencode=no commit-signing >output 2>err &&
+ ! grep ^gpgsig output &&
+ grep "^encoding ISO-8859-1" output &&
+ test -s err &&
+ sed "s/commit-signing/commit-strip-signing/" output | (
+ cd new &&
+ git fast-import &&
+ STRIPPED=$(git rev-parse --verify refs/heads/commit-strip-signing) &&
+ test $COMMIT_SIGNING != $STRIPPED
+ )
+
+'
+
+test_expect_success GPG 'signed-commits=abort' '
+
+ test_must_fail git fast-export --signed-commits=abort commit-signing
+
+'
+
+test_expect_success GPG 'signed-commits=verbatim' '
+
+ git fast-export --signed-commits=verbatim --reencode=no commit-signing >output &&
+ grep "^gpgsig sha" output &&
+ grep "encoding ISO-8859-1" output &&
+ (
+ cd new &&
+ git fast-import &&
+ STRIPPED=$(git rev-parse --verify refs/heads/commit-signing) &&
+ test $COMMIT_SIGNING = $STRIPPED
+ ) <output
+
+'
+
+test_expect_success GPG 'signed-commits=warn-verbatim' '
+
+ git fast-export --signed-commits=warn-verbatim --reencode=no commit-signing >output 2>err &&
+ grep "^gpgsig sha" output &&
+ grep "encoding ISO-8859-1" output &&
+ test -s err &&
+ (
+ cd new &&
+ git fast-import &&
+ STRIPPED=$(git rev-parse --verify refs/heads/commit-signing) &&
+ test $COMMIT_SIGNING = $STRIPPED
+ ) <output
+
+'
+
+test_expect_success GPG 'signed-commits=strip' '
+
+ git fast-export --signed-commits=strip --reencode=no commit-signing >output &&
+ ! grep ^gpgsig output &&
+ grep "^encoding ISO-8859-1" output &&
+ sed "s/commit-signing/commit-strip-signing/" output | (
+ cd new &&
+ git fast-import &&
+ STRIPPED=$(git rev-parse --verify refs/heads/commit-strip-signing) &&
+ test $COMMIT_SIGNING != $STRIPPED
+ )
+
+'
+
+test_expect_success GPG 'signed-commits=warn-strip' '
+
+ git fast-export --signed-commits=warn-strip --reencode=no commit-signing >output 2>err &&
+ ! grep ^gpgsig output &&
+ grep "^encoding ISO-8859-1" output &&
+ test -s err &&
+ sed "s/commit-signing/commit-strip-signing/" output | (
+ cd new &&
+ git fast-import &&
+ STRIPPED=$(git rev-parse --verify refs/heads/commit-strip-signing) &&
+ test $COMMIT_SIGNING != $STRIPPED
+ )
+
+'
+
test_expect_success 'setup submodule' '
test_config_global protocol.file.allow always &&
git checkout -f main &&
+ test_might_fail git update-ref -d refs/heads/commit-signing &&
mkdir sub &&
(
cd sub &&
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 51bd750837..343b8cd191 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -149,7 +149,8 @@ fi
test_expect_success 'setup for __git_find_repo_path/__gitdir tests' '
mkdir -p subdir/subsubdir &&
mkdir -p non-repo &&
- git init -b main otherrepo
+ git init -b main otherrepo &&
+ git init -b main slashrepo
'
test_expect_success '__git_find_repo_path - from command line (through $__git_dir)' '
@@ -455,6 +456,32 @@ test_expect_success '__git_dequote - open double quote' '
'
+test_expect_success '__git_count_path_components - no slashes' '
+ echo 1 >expected &&
+ __git_count_path_components a >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__git_count_path_components - relative' '
+ echo 3 >expected &&
+ __git_count_path_components a/b/c >"$actual" &&
+ test_cmp expected "$actual"
+
+'
+
+test_expect_success '__git_count_path_components - absolute' '
+ echo 3 >expected &&
+ __git_count_path_components /a/b/c >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__git_count_path_components - trailing slash' '
+ echo 3 >expected &&
+ __git_count_path_components a/b/c/ >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+
test_expect_success '__gitcomp_direct - puts everything into COMPREPLY as-is' '
sed -e "s/Z$//g" >expected <<-EOF &&
with-trailing-space Z
@@ -648,6 +675,13 @@ test_expect_success 'setup for ref completion' '
) &&
git remote add other "$ROOT/otherrepo/.git" &&
git fetch --no-tags other &&
+ (
+ cd slashrepo &&
+ git commit --allow-empty -m initial &&
+ git branch -m main branch/with/slash
+ ) &&
+ git remote add remote/with/slash "$ROOT/slashrepo/.git" &&
+ git fetch --no-tags remote/with/slash &&
rm -f .git/FETCH_HEAD &&
git init thirdrepo
'
@@ -660,6 +694,8 @@ test_expect_success '__git_refs - simple' '
other/HEAD
other/branch-in-other
other/main-in-other
+ remote/with/slash/HEAD
+ remote/with/slash/branch/with/slash
matching-tag
EOF
(
@@ -676,6 +712,8 @@ test_expect_success '__git_refs - full refs' '
refs/remotes/other/HEAD
refs/remotes/other/branch-in-other
refs/remotes/other/main-in-other
+ refs/remotes/remote/with/slash/HEAD
+ refs/remotes/remote/with/slash/branch/with/slash
refs/tags/matching-tag
EOF
(
@@ -741,6 +779,19 @@ test_expect_success '__git_refs - configured remote' '
test_cmp expected "$actual"
'
+test_expect_success '__git_refs - configured remote - with slash' '
+ cat >expected <<-EOF &&
+ HEAD
+ HEAD
+ branch/with/slash
+ EOF
+ (
+ cur= &&
+ __git_refs remote/with/slash >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
test_expect_success '__git_refs - configured remote - full refs' '
cat >expected <<-EOF &&
HEAD
@@ -883,17 +934,19 @@ test_expect_success '__git_refs - unique remote branches for git checkout DWIMer
other/ambiguous
other/branch-in-other
other/main-in-other
- remote/ambiguous
- remote/branch-in-remote
+ remote/with/slash/HEAD
+ remote/with/slash/ambiguous
+ remote/with/slash/branch-in-remote
+ remote/with/slash/branch/with/slash
matching-tag
- HEAD
branch-in-other
branch-in-remote
+ branch/with/slash
main-in-other
EOF
for remote_ref in refs/remotes/other/ambiguous \
- refs/remotes/remote/ambiguous \
- refs/remotes/remote/branch-in-remote
+ refs/remotes/remote/with/slash/ambiguous \
+ refs/remotes/remote/with/slash/branch-in-remote
do
git update-ref $remote_ref main &&
test_when_finished "git update-ref -d $remote_ref" || return 1
@@ -913,6 +966,8 @@ test_expect_success '__git_refs - after --opt=' '
other/HEAD
other/branch-in-other
other/main-in-other
+ remote/with/slash/HEAD
+ remote/with/slash/branch/with/slash
matching-tag
EOF
(
@@ -929,6 +984,8 @@ test_expect_success '__git_refs - after --opt= - full refs' '
refs/remotes/other/HEAD
refs/remotes/other/branch-in-other
refs/remotes/other/main-in-other
+ refs/remotes/remote/with/slash/HEAD
+ refs/remotes/remote/with/slash/branch/with/slash
refs/tags/matching-tag
EOF
(
@@ -946,6 +1003,8 @@ test_expect_success '__git refs - excluding refs' '
^other/HEAD
^other/branch-in-other
^other/main-in-other
+ ^remote/with/slash/HEAD
+ ^remote/with/slash/branch/with/slash
^matching-tag
EOF
(
@@ -962,6 +1021,8 @@ test_expect_success '__git refs - excluding full refs' '
^refs/remotes/other/HEAD
^refs/remotes/other/branch-in-other
^refs/remotes/other/main-in-other
+ ^refs/remotes/remote/with/slash/HEAD
+ ^refs/remotes/remote/with/slash/branch/with/slash
^refs/tags/matching-tag
EOF
(
@@ -989,6 +1050,8 @@ test_expect_success '__git_refs - do not filter refs unless told so' '
other/branch-in-other
other/main-in-other
other/matching/branch-in-other
+ remote/with/slash/HEAD
+ remote/with/slash/branch/with/slash
matching-tag
matching/tag
EOF
@@ -1109,6 +1172,8 @@ test_expect_success '__git_complete_refs - simple' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
matching-tag Z
EOF
(
@@ -1147,6 +1212,20 @@ test_expect_success '__git_complete_refs - remote' '
test_cmp expected out
'
+test_expect_success '__git_complete_refs - remote - with slash' '
+ sed -e "s/Z$//" >expected <<-EOF &&
+ HEAD Z
+ HEAD Z
+ branch/with/slash Z
+ EOF
+ (
+ cur= &&
+ __git_complete_refs --remote=remote/with/slash &&
+ print_comp
+ ) &&
+ test_cmp expected out
+'
+
test_expect_success '__git_complete_refs - track' '
sed -e "s/Z$//" >expected <<-EOF &&
HEAD Z
@@ -1155,9 +1234,11 @@ test_expect_success '__git_complete_refs - track' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
matching-tag Z
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main-in-other Z
EOF
(
@@ -1202,6 +1283,8 @@ test_expect_success '__git_complete_refs - suffix' '
other/HEAD.
other/branch-in-other.
other/main-in-other.
+ remote/with/slash/HEAD.
+ remote/with/slash/branch/with/slash.
matching-tag.
EOF
(
@@ -1227,6 +1310,20 @@ test_expect_success '__git_complete_fetch_refspecs - simple' '
test_cmp expected out
'
+test_expect_success '__git_complete_fetch_refspecs - with slash' '
+ sed -e "s/Z$//" >expected <<-EOF &&
+ HEAD:HEAD Z
+ HEAD:HEAD Z
+ branch/with/slash:branch/with/slash Z
+ EOF
+ (
+ cur= &&
+ __git_complete_fetch_refspecs remote/with/slash &&
+ print_comp
+ ) &&
+ test_cmp expected out
+'
+
test_expect_success '__git_complete_fetch_refspecs - matching' '
sed -e "s/Z$//" >expected <<-EOF &&
branch-in-other:branch-in-other Z
@@ -1307,8 +1404,8 @@ test_expect_success '__git_complete_worktree_paths with -C' '
test_expect_success 'git switch - with no options, complete local branches and unique remote branch names for DWIM logic' '
test_completion "git switch " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1454,8 +1551,8 @@ test_expect_success 'git-bisect - existing view subcommand is recognized and ena
test_expect_success 'git checkout - completes refs and unique remote branches for DWIM' '
test_completion "git checkout " <<-\EOF
HEAD Z
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1463,6 +1560,8 @@ test_expect_success 'git checkout - completes refs and unique remote branches fo
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1482,8 +1581,8 @@ test_expect_success 'git switch - with GIT_COMPLETION_CHECKOUT_NO_GUESS=1, compl
test_expect_success 'git switch - --guess overrides GIT_COMPLETION_CHECKOUT_NO_GUESS=1, complete local branches and unique remote names for DWIM logic' '
GIT_COMPLETION_CHECKOUT_NO_GUESS=1 test_completion "git switch --guess " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1492,8 +1591,8 @@ test_expect_success 'git switch - --guess overrides GIT_COMPLETION_CHECKOUT_NO_G
test_expect_success 'git switch - a later --guess overrides previous --no-guess, complete local and remote unique branches for DWIM' '
test_completion "git switch --no-guess --guess " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1516,14 +1615,16 @@ test_expect_success 'git checkout - with GIT_COMPLETION_NO_GUESS=1 only complete
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
test_expect_success 'git checkout - --guess overrides GIT_COMPLETION_NO_GUESS=1, complete refs and unique remote branches for DWIM' '
GIT_COMPLETION_CHECKOUT_NO_GUESS=1 test_completion "git checkout --guess " <<-\EOF
HEAD Z
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1531,6 +1632,8 @@ test_expect_success 'git checkout - --guess overrides GIT_COMPLETION_NO_GUESS=1,
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1543,14 +1646,16 @@ test_expect_success 'git checkout - with --no-guess, only completes refs' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
test_expect_success 'git checkout - a later --guess overrides previous --no-guess, complete refs and unique remote branches for DWIM' '
test_completion "git checkout --no-guess --guess " <<-\EOF
HEAD Z
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1558,6 +1663,8 @@ test_expect_success 'git checkout - a later --guess overrides previous --no-gues
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1570,6 +1677,8 @@ test_expect_success 'git checkout - a later --no-guess overrides previous --gues
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1583,6 +1692,8 @@ test_expect_success 'git checkout - with checkout.guess = false, only completes
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1590,8 +1701,8 @@ test_expect_success 'git checkout - with checkout.guess = true, completes refs a
test_config checkout.guess true &&
test_completion "git checkout " <<-\EOF
HEAD Z
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1599,6 +1710,8 @@ test_expect_success 'git checkout - with checkout.guess = true, completes refs a
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1606,8 +1719,8 @@ test_expect_success 'git checkout - a later --guess overrides previous checkout.
test_config checkout.guess false &&
test_completion "git checkout --guess " <<-\EOF
HEAD Z
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -1615,6 +1728,8 @@ test_expect_success 'git checkout - a later --guess overrides previous checkout.
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1628,6 +1743,8 @@ test_expect_success 'git checkout - a later --no-guess overrides previous checko
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1640,6 +1757,8 @@ test_expect_success 'git switch - with --detach, complete all references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1652,6 +1771,8 @@ test_expect_success 'git checkout - with --detach, complete only references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1824,6 +1945,8 @@ test_expect_success 'git switch - with -d, complete all references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1836,6 +1959,8 @@ test_expect_success 'git checkout - with -d, complete only references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1844,11 +1969,15 @@ test_expect_success 'git switch - with --track, complete only remote branches' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
test_completion "git switch -t " <<-\EOF
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1857,11 +1986,15 @@ test_expect_success 'git checkout - with --track, complete only remote branches'
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
test_completion "git checkout -t " <<-\EOF
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1881,6 +2014,8 @@ test_expect_success 'git checkout - with --no-track, complete only local referen
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1893,6 +2028,8 @@ test_expect_success 'git switch - with -c, complete all references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1905,6 +2042,8 @@ test_expect_success 'git switch - with -C, complete all references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1917,6 +2056,8 @@ test_expect_success 'git switch - with -c and --track, complete all references'
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1929,6 +2070,8 @@ test_expect_success 'git switch - with -C and --track, complete all references'
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1941,6 +2084,8 @@ test_expect_success 'git switch - with -c and --no-track, complete all reference
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1953,6 +2098,8 @@ test_expect_success 'git switch - with -C and --no-track, complete all reference
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1965,6 +2112,8 @@ test_expect_success 'git checkout - with -b, complete all references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1977,6 +2126,8 @@ test_expect_success 'git checkout - with -B, complete all references' '
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -1989,6 +2140,8 @@ test_expect_success 'git checkout - with -b and --track, complete all references
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -2001,6 +2154,8 @@ test_expect_success 'git checkout - with -B and --track, complete all references
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -2013,6 +2168,8 @@ test_expect_success 'git checkout - with -b and --no-track, complete all referen
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -2025,13 +2182,15 @@ test_expect_success 'git checkout - with -B and --no-track, complete all referen
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
test_expect_success 'git switch - for -c, complete local branches and unique remote branches' '
test_completion "git switch -c " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -2040,8 +2199,8 @@ test_expect_success 'git switch - for -c, complete local branches and unique rem
test_expect_success 'git switch - for -C, complete local branches and unique remote branches' '
test_completion "git switch -C " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -2078,8 +2237,8 @@ test_expect_success 'git switch - for -C with --no-track, complete local branche
test_expect_success 'git checkout - for -b, complete local branches and unique remote branches' '
test_completion "git checkout -b " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -2088,8 +2247,8 @@ test_expect_success 'git checkout - for -b, complete local branches and unique r
test_expect_success 'git checkout - for -B, complete local branches and unique remote branches' '
test_completion "git checkout -B " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -2126,8 +2285,8 @@ test_expect_success 'git checkout - for -B with --no-track, complete local branc
test_expect_success 'git switch - with --orphan completes local branch names and unique remote branch names' '
test_completion "git switch --orphan " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -2142,8 +2301,8 @@ test_expect_success 'git switch - --orphan with branch already provided complete
test_expect_success 'git checkout - with --orphan completes local branch names and unique remote branch names' '
test_completion "git checkout --orphan " <<-\EOF
- HEAD Z
branch-in-other Z
+ branch/with/slash Z
main Z
main-in-other Z
matching-branch Z
@@ -2159,6 +2318,8 @@ test_expect_success 'git checkout - --orphan with branch already provided comple
other/HEAD Z
other/branch-in-other Z
other/main-in-other Z
+ remote/with/slash/HEAD Z
+ remote/with/slash/branch/with/slash Z
EOF
'
@@ -2173,7 +2334,8 @@ test_expect_success 'git restore completes modified files' '
test_expect_success 'teardown after ref completion' '
git branch -d matching-branch &&
git tag -d matching-tag &&
- git remote remove other
+ git remote remove other &&
+ git remote remove remote/with/slash
'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 79377bc0fc..16eaaaf4c3 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -773,6 +773,8 @@ mkdir -p "$TRASH_DIRECTORY/prereq-test-dir-'"$1"'" &&
rm -rf "$TRASH_DIRECTORY/prereq-test-dir-$1"
if test "$eval_ret" = 0; then
say >&3 "prerequisite $1 ok"
+ elif test "$eval_ret" = 125; then
+ :;
else
say >&3 "prerequisite $1 not satisfied"
fi
@@ -811,6 +813,9 @@ test_have_prereq () {
if test_run_lazy_prereq_ "$prerequisite" "$script"
then
test_set_prereq $prerequisite
+ elif test $? = 125
+ then
+ BUG "Do not use $prerequisite"
fi
lazily_tested_prereq="$lazily_tested_prereq$prerequisite "
esac
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 9001ed3a64..fffbfb89ef 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1862,8 +1862,13 @@ test_lazy_prereq CURL '
curl --version
'
+test_lazy_prereq WITH_BREAKING_CHANGES '
+ test -n "$WITH_BREAKING_CHANGES"
+'
+
test_lazy_prereq WITHOUT_BREAKING_CHANGES '
- test -z "$WITH_BREAKING_CHANGES"
+ # Signal that this prereq should not be used.
+ exit 125
'
# SHA1 is a test if the hash algorithm in use is SHA-1. This is both for tests
diff --git a/t/unit-tests/lib-oid.c b/t/unit-tests/lib-oid.c
index 8f0ccac532..e0b3180f23 100644
--- a/t/unit-tests/lib-oid.c
+++ b/t/unit-tests/lib-oid.c
@@ -1,9 +1,9 @@
-#include "test-lib.h"
+#include "unit-test.h"
#include "lib-oid.h"
#include "strbuf.h"
#include "hex.h"
-int init_hash_algo(void)
+int cl_setup_hash_algo(void)
{
static int algo = -1;
@@ -11,42 +11,32 @@ int init_hash_algo(void)
const char *algo_name = getenv("GIT_TEST_DEFAULT_HASH");
algo = algo_name ? hash_algo_by_name(algo_name) : GIT_HASH_SHA1;
- if (!check(algo != GIT_HASH_UNKNOWN))
- test_msg("BUG: invalid GIT_TEST_DEFAULT_HASH value ('%s')",
- algo_name);
+ cl_assert(algo != GIT_HASH_UNKNOWN);
}
return algo;
}
-static int get_oid_arbitrary_hex_algop(const char *hex, struct object_id *oid,
+static void cl_parse_oid(const char *hex, struct object_id *oid,
const struct git_hash_algo *algop)
{
- int ret;
size_t sz = strlen(hex);
struct strbuf buf = STRBUF_INIT;
- if (!check(sz <= algop->hexsz)) {
- test_msg("BUG: hex string (%s) bigger than maximum allowed (%lu)",
- hex, (unsigned long)algop->hexsz);
- return -1;
- }
+ cl_assert(sz <= algop->hexsz);
strbuf_add(&buf, hex, sz);
strbuf_addchars(&buf, '0', algop->hexsz - sz);
- ret = get_oid_hex_algop(buf.buf, oid, algop);
- if (!check_int(ret, ==, 0))
- test_msg("BUG: invalid hex input (%s) provided", hex);
+ cl_assert_equal_i(get_oid_hex_algop(buf.buf, oid, algop), 0);
strbuf_release(&buf);
- return ret;
}
-int get_oid_arbitrary_hex(const char *hex, struct object_id *oid)
+
+void cl_parse_any_oid(const char *hex, struct object_id *oid)
{
- int hash_algo = init_hash_algo();
+ int hash_algo = cl_setup_hash_algo();
- if (!check_int(hash_algo, !=, GIT_HASH_UNKNOWN))
- return -1;
- return get_oid_arbitrary_hex_algop(hex, oid, &hash_algos[hash_algo]);
+ cl_assert(hash_algo != GIT_HASH_UNKNOWN);
+ cl_parse_oid(hex, oid, &hash_algos[hash_algo]);
}
diff --git a/t/unit-tests/lib-oid.h b/t/unit-tests/lib-oid.h
index 4e77c04bd2..4031775104 100644
--- a/t/unit-tests/lib-oid.h
+++ b/t/unit-tests/lib-oid.h
@@ -5,6 +5,7 @@
/*
* Convert arbitrary hex string to object_id.
+ *
* For example, passing "abc12" will generate
* "abc1200000000000000000000000000000000000" hex of length 40 for SHA-1 and
* create object_id with that.
@@ -12,14 +13,16 @@
* algo is not allowed. The hash algo is decided based on GIT_TEST_DEFAULT_HASH
* environment variable.
*/
-int get_oid_arbitrary_hex(const char *s, struct object_id *oid);
+
+void cl_parse_any_oid (const char *s, struct object_id *oid);
/*
* Returns one of GIT_HASH_{SHA1, SHA256, UNKNOWN} based on the value of
* GIT_TEST_DEFAULT_HASH environment variable. The fallback value in the
* absence of GIT_TEST_DEFAULT_HASH is GIT_HASH_SHA1. It also uses
- * check(algo != GIT_HASH_UNKNOWN) before returning to verify if the
+ * cl_assert(algo != GIT_HASH_UNKNOWN) before returning to verify if the
* GIT_TEST_DEFAULT_HASH's value is valid or not.
*/
-int init_hash_algo(void);
+
+int cl_setup_hash_algo(void);
#endif /* LIB_OID_H */
diff --git a/t/unit-tests/t-oid-array.c b/t/unit-tests/t-oid-array.c
deleted file mode 100644
index 45b59a2a51..0000000000
--- a/t/unit-tests/t-oid-array.c
+++ /dev/null
@@ -1,126 +0,0 @@
-#define USE_THE_REPOSITORY_VARIABLE
-
-#include "test-lib.h"
-#include "lib-oid.h"
-#include "oid-array.h"
-#include "hex.h"
-
-static int fill_array(struct oid_array *array, const char *hexes[], size_t n)
-{
- for (size_t i = 0; i < n; i++) {
- struct object_id oid;
-
- if (!check_int(get_oid_arbitrary_hex(hexes[i], &oid), ==, 0))
- return -1;
- oid_array_append(array, &oid);
- }
- if (!check_uint(array->nr, ==, n))
- return -1;
- return 0;
-}
-
-static int add_to_oid_array(const struct object_id *oid, void *data)
-{
- struct oid_array *array = data;
-
- oid_array_append(array, oid);
- return 0;
-}
-
-static void t_enumeration(const char **input_args, size_t input_sz,
- const char **expect_args, size_t expect_sz)
-{
- struct oid_array input = OID_ARRAY_INIT, expect = OID_ARRAY_INIT,
- actual = OID_ARRAY_INIT;
- size_t i;
-
- if (fill_array(&input, input_args, input_sz))
- return;
- if (fill_array(&expect, expect_args, expect_sz))
- return;
-
- oid_array_for_each_unique(&input, add_to_oid_array, &actual);
- if (!check_uint(actual.nr, ==, expect.nr))
- return;
-
- for (i = 0; i < actual.nr; i++) {
- if (!check(oideq(&actual.oid[i], &expect.oid[i])))
- test_msg("expected: %s\n got: %s\n index: %" PRIuMAX,
- oid_to_hex(&expect.oid[i]), oid_to_hex(&actual.oid[i]),
- (uintmax_t)i);
- }
-
- oid_array_clear(&actual);
- oid_array_clear(&input);
- oid_array_clear(&expect);
-}
-
-#define TEST_ENUMERATION(input, expect, desc) \
- TEST(t_enumeration(input, ARRAY_SIZE(input), expect, ARRAY_SIZE(expect)), \
- desc " works")
-
-static void t_lookup(const char **input_hexes, size_t n, const char *query_hex,
- int lower_bound, int upper_bound)
-{
- struct oid_array array = OID_ARRAY_INIT;
- struct object_id oid_query;
- int ret;
-
- if (!check_int(get_oid_arbitrary_hex(query_hex, &oid_query), ==, 0))
- return;
- if (fill_array(&array, input_hexes, n))
- return;
- ret = oid_array_lookup(&array, &oid_query);
-
- if (!check_int(ret, <=, upper_bound) ||
- !check_int(ret, >=, lower_bound))
- test_msg("oid query for lookup: %s", oid_to_hex(&oid_query));
-
- oid_array_clear(&array);
-}
-
-#define TEST_LOOKUP(input_hexes, query, lower_bound, upper_bound, desc) \
- TEST(t_lookup(input_hexes, ARRAY_SIZE(input_hexes), query, \
- lower_bound, upper_bound), \
- desc " works")
-
-static void setup(void)
-{
- /* The hash algo is used by oid_array_lookup() internally */
- int algo = init_hash_algo();
- if (check_int(algo, !=, GIT_HASH_UNKNOWN))
- repo_set_hash_algo(the_repository, algo);
-}
-
-int cmd_main(int argc UNUSED, const char **argv UNUSED)
-{
- const char *arr_input[] = { "88", "44", "aa", "55" };
- const char *arr_input_dup[] = { "88", "44", "aa", "55",
- "88", "44", "aa", "55",
- "88", "44", "aa", "55" };
- const char *res_sorted[] = { "44", "55", "88", "aa" };
- const char *nearly_55;
-
- if (!TEST(setup(), "setup"))
- test_skip_all("hash algo initialization failed");
-
- TEST_ENUMERATION(arr_input, res_sorted, "ordered enumeration");
- TEST_ENUMERATION(arr_input_dup, res_sorted,
- "ordered enumeration with duplicate suppression");
-
- TEST_LOOKUP(arr_input, "55", 1, 1, "lookup");
- TEST_LOOKUP(arr_input, "33", INT_MIN, -1, "lookup non-existent entry");
- TEST_LOOKUP(arr_input_dup, "55", 3, 5, "lookup with duplicates");
- TEST_LOOKUP(arr_input_dup, "66", INT_MIN, -1,
- "lookup non-existent entry with duplicates");
-
- nearly_55 = init_hash_algo() == GIT_HASH_SHA1 ?
- "5500000000000000000000000000000000000001" :
- "5500000000000000000000000000000000000000000000000000000000000001";
- TEST_LOOKUP(((const char *[]){ "55", nearly_55 }), "55", 0, 0,
- "lookup with almost duplicate values");
- TEST_LOOKUP(((const char *[]){ "55", "55" }), "55", 0, 1,
- "lookup with single duplicate value");
-
- return test_done();
-}
diff --git a/t/unit-tests/t-oidmap.c b/t/unit-tests/t-oidmap.c
deleted file mode 100644
index b22e52d08b..0000000000
--- a/t/unit-tests/t-oidmap.c
+++ /dev/null
@@ -1,181 +0,0 @@
-#include "test-lib.h"
-#include "lib-oid.h"
-#include "oidmap.h"
-#include "hash.h"
-#include "hex.h"
-
-/*
- * Elements we will put in oidmap structs are made of a key: the entry.oid
- * field, which is of type struct object_id, and a value: the name field (could
- * be a refname for example).
- */
-struct test_entry {
- struct oidmap_entry entry;
- char name[FLEX_ARRAY];
-};
-
-static const char *const key_val[][2] = { { "11", "one" },
- { "22", "two" },
- { "33", "three" } };
-
-static void setup(void (*f)(struct oidmap *map))
-{
- struct oidmap map = OIDMAP_INIT;
- int ret = 0;
-
- for (size_t i = 0; i < ARRAY_SIZE(key_val); i++){
- struct test_entry *entry;
-
- FLEX_ALLOC_STR(entry, name, key_val[i][1]);
- if ((ret = get_oid_arbitrary_hex(key_val[i][0], &entry->entry.oid))) {
- free(entry);
- break;
- }
- entry = oidmap_put(&map, entry);
- if (!check(entry == NULL))
- free(entry);
- }
-
- if (!ret)
- f(&map);
- oidmap_free(&map, 1);
-}
-
-static void t_replace(struct oidmap *map)
-{
- struct test_entry *entry, *prev;
-
- FLEX_ALLOC_STR(entry, name, "un");
- if (get_oid_arbitrary_hex("11", &entry->entry.oid))
- return;
- prev = oidmap_put(map, entry);
- if (!check(prev != NULL))
- return;
- check_str(prev->name, "one");
- free(prev);
-
- FLEX_ALLOC_STR(entry, name, "deux");
- if (get_oid_arbitrary_hex("22", &entry->entry.oid))
- return;
- prev = oidmap_put(map, entry);
- if (!check(prev != NULL))
- return;
- check_str(prev->name, "two");
- free(prev);
-}
-
-static void t_get(struct oidmap *map)
-{
- struct test_entry *entry;
- struct object_id oid;
-
- if (get_oid_arbitrary_hex("22", &oid))
- return;
- entry = oidmap_get(map, &oid);
- if (!check(entry != NULL))
- return;
- check_str(entry->name, "two");
-
- if (get_oid_arbitrary_hex("44", &oid))
- return;
- check(oidmap_get(map, &oid) == NULL);
-
- if (get_oid_arbitrary_hex("11", &oid))
- return;
- entry = oidmap_get(map, &oid);
- if (!check(entry != NULL))
- return;
- check_str(entry->name, "one");
-}
-
-static void t_remove(struct oidmap *map)
-{
- struct test_entry *entry;
- struct object_id oid;
-
- if (get_oid_arbitrary_hex("11", &oid))
- return;
- entry = oidmap_remove(map, &oid);
- if (!check(entry != NULL))
- return;
- check_str(entry->name, "one");
- check(oidmap_get(map, &oid) == NULL);
- free(entry);
-
- if (get_oid_arbitrary_hex("22", &oid))
- return;
- entry = oidmap_remove(map, &oid);
- if (!check(entry != NULL))
- return;
- check_str(entry->name, "two");
- check(oidmap_get(map, &oid) == NULL);
- free(entry);
-
- if (get_oid_arbitrary_hex("44", &oid))
- return;
- check(oidmap_remove(map, &oid) == NULL);
-}
-
-static int key_val_contains(struct test_entry *entry, char seen[])
-{
- for (size_t i = 0; i < ARRAY_SIZE(key_val); i++) {
- struct object_id oid;
-
- if (get_oid_arbitrary_hex(key_val[i][0], &oid))
- return -1;
-
- if (oideq(&entry->entry.oid, &oid)) {
- if (seen[i])
- return 2;
- seen[i] = 1;
- return 0;
- }
- }
- return 1;
-}
-
-static void t_iterate(struct oidmap *map)
-{
- struct oidmap_iter iter;
- struct test_entry *entry;
- char seen[ARRAY_SIZE(key_val)] = { 0 };
- int count = 0;
-
- oidmap_iter_init(map, &iter);
- while ((entry = oidmap_iter_next(&iter))) {
- int ret;
- if (!check_int((ret = key_val_contains(entry, seen)), ==, 0)) {
- switch (ret) {
- case -1:
- break; /* error message handled by get_oid_arbitrary_hex() */
- case 1:
- test_msg("obtained entry was not given in the input\n"
- " name: %s\n oid: %s\n",
- entry->name, oid_to_hex(&entry->entry.oid));
- break;
- case 2:
- test_msg("duplicate entry detected\n"
- " name: %s\n oid: %s\n",
- entry->name, oid_to_hex(&entry->entry.oid));
- break;
- default:
- test_msg("BUG: invalid return value (%d) from key_val_contains()",
- ret);
- break;
- }
- } else {
- count++;
- }
- }
- check_int(count, ==, ARRAY_SIZE(key_val));
- check_int(hashmap_get_size(&map->map), ==, ARRAY_SIZE(key_val));
-}
-
-int cmd_main(int argc UNUSED, const char **argv UNUSED)
-{
- TEST(setup(t_replace), "replace works");
- TEST(setup(t_get), "get works");
- TEST(setup(t_remove), "remove works");
- TEST(setup(t_iterate), "iterate works");
- return test_done();
-}
diff --git a/t/unit-tests/t-oidtree.c b/t/unit-tests/t-oidtree.c
deleted file mode 100644
index a38754b066..0000000000
--- a/t/unit-tests/t-oidtree.c
+++ /dev/null
@@ -1,122 +0,0 @@
-#include "test-lib.h"
-#include "lib-oid.h"
-#include "oidtree.h"
-#include "hash.h"
-#include "hex.h"
-#include "strvec.h"
-
-#define FILL_TREE(tree, ...) \
- do { \
- const char *hexes[] = { __VA_ARGS__ }; \
- if (fill_tree_loc(tree, hexes, ARRAY_SIZE(hexes))) \
- return; \
- } while (0)
-
-static int fill_tree_loc(struct oidtree *ot, const char *hexes[], size_t n)
-{
- for (size_t i = 0; i < n; i++) {
- struct object_id oid;
- if (!check_int(get_oid_arbitrary_hex(hexes[i], &oid), ==, 0))
- return -1;
- oidtree_insert(ot, &oid);
- }
- return 0;
-}
-
-static void check_contains(struct oidtree *ot, const char *hex, int expected)
-{
- struct object_id oid;
-
- if (!check_int(get_oid_arbitrary_hex(hex, &oid), ==, 0))
- return;
- if (!check_int(oidtree_contains(ot, &oid), ==, expected))
- test_msg("oid: %s", oid_to_hex(&oid));
-}
-
-struct expected_hex_iter {
- size_t i;
- struct strvec expected_hexes;
- const char *query;
-};
-
-static enum cb_next check_each_cb(const struct object_id *oid, void *data)
-{
- struct expected_hex_iter *hex_iter = data;
- struct object_id expected;
-
- if (!check_int(hex_iter->i, <, hex_iter->expected_hexes.nr)) {
- test_msg("error: extraneous callback for query: ('%s'), object_id: ('%s')",
- hex_iter->query, oid_to_hex(oid));
- return CB_BREAK;
- }
-
- if (!check_int(get_oid_arbitrary_hex(hex_iter->expected_hexes.v[hex_iter->i],
- &expected), ==, 0))
- ; /* the data is bogus and cannot be used */
- else if (!check(oideq(oid, &expected)))
- test_msg("expected: %s\n got: %s\n query: %s",
- oid_to_hex(&expected), oid_to_hex(oid), hex_iter->query);
-
- hex_iter->i += 1;
- return CB_CONTINUE;
-}
-
-LAST_ARG_MUST_BE_NULL
-static void check_each(struct oidtree *ot, const char *query, ...)
-{
- struct object_id oid;
- struct expected_hex_iter hex_iter = { .expected_hexes = STRVEC_INIT,
- .query = query };
- const char *arg;
- va_list hex_args;
-
- va_start(hex_args, query);
- while ((arg = va_arg(hex_args, const char *)))
- strvec_push(&hex_iter.expected_hexes, arg);
- va_end(hex_args);
-
- if (!check_int(get_oid_arbitrary_hex(query, &oid), ==, 0))
- return;
- oidtree_each(ot, &oid, strlen(query), check_each_cb, &hex_iter);
-
- if (!check_int(hex_iter.i, ==, hex_iter.expected_hexes.nr))
- test_msg("error: could not find some 'object_id's for query ('%s')", query);
- strvec_clear(&hex_iter.expected_hexes);
-}
-
-static void setup(void (*f)(struct oidtree *ot))
-{
- struct oidtree ot;
-
- oidtree_init(&ot);
- f(&ot);
- oidtree_clear(&ot);
-}
-
-static void t_contains(struct oidtree *ot)
-{
- FILL_TREE(ot, "444", "1", "2", "3", "4", "5", "a", "b", "c", "d", "e");
- check_contains(ot, "44", 0);
- check_contains(ot, "441", 0);
- check_contains(ot, "440", 0);
- check_contains(ot, "444", 1);
- check_contains(ot, "4440", 1);
- check_contains(ot, "4444", 0);
-}
-
-static void t_each(struct oidtree *ot)
-{
- FILL_TREE(ot, "f", "9", "8", "123", "321", "320", "a", "b", "c", "d", "e");
- check_each(ot, "12300", "123", NULL);
- check_each(ot, "3211", NULL); /* should not reach callback */
- check_each(ot, "3210", "321", NULL);
- check_each(ot, "32100", "321", NULL);
- check_each(ot, "32", "320", "321", NULL);
-}
-
-int cmd_main(int argc UNUSED, const char **argv UNUSED)
-{
- TEST(setup(t_contains), "oidtree insert and contains works");
- TEST(setup(t_each), "oidtree each works");
- return test_done();
-}
diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c
index 9ba7eb05ad..c9e751e49e 100644
--- a/t/unit-tests/t-reftable-basics.c
+++ b/t/unit-tests/t-reftable-basics.c
@@ -128,12 +128,30 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
reftable_buf_release(&b);
}
- if_test ("put_be24 and get_be24 work") {
+ if_test ("reftable_put_be64 and reftable_get_be64 work") {
+ uint64_t in = 0x1122334455667788;
+ uint8_t dest[8];
+ uint64_t out;
+ reftable_put_be64(dest, in);
+ out = reftable_get_be64(dest);
+ check_int(in, ==, out);
+ }
+
+ if_test ("reftable_put_be32 and reftable_get_be32 work") {
+ uint32_t in = 0x11223344;
+ uint8_t dest[4];
+ uint32_t out;
+ reftable_put_be32(dest, in);
+ out = reftable_get_be32(dest);
+ check_int(in, ==, out);
+ }
+
+ if_test ("reftable_put_be24 and reftable_get_be24 work") {
uint32_t in = 0x112233;
uint8_t dest[3];
uint32_t out;
- put_be24(dest, in);
- out = get_be24(dest);
+ reftable_put_be24(dest, in);
+ out = reftable_get_be24(dest);
check_int(in, ==, out);
}
@@ -141,8 +159,8 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
uint32_t in = 0xfef1;
uint8_t dest[3];
uint32_t out;
- put_be16(dest, in);
- out = get_be16(dest);
+ reftable_put_be16(dest, in);
+ out = reftable_get_be16(dest);
check_int(in, ==, out);
}
diff --git a/t/unit-tests/t-reftable-pq.c b/t/unit-tests/t-reftable-pq.c
index f3f8a0cdf3..c128fe8616 100644
--- a/t/unit-tests/t-reftable-pq.c
+++ b/t/unit-tests/t-reftable-pq.c
@@ -21,7 +21,9 @@ static void merged_iter_pqueue_check(const struct merged_iter_pqueue *pq)
static int pq_entry_equal(struct pq_entry *a, struct pq_entry *b)
{
- return !reftable_record_cmp(a->rec, b->rec) && (a->index == b->index);
+ int cmp;
+ check(!reftable_record_cmp(a->rec, b->rec, &cmp));
+ return !cmp && (a->index == b->index);
}
static void t_pq_record(void)
@@ -32,7 +34,7 @@ static void t_pq_record(void)
char *last = NULL;
for (i = 0; i < N; i++) {
- reftable_record_init(&recs[i], BLOCK_TYPE_REF);
+ check(!reftable_record_init(&recs[i], BLOCK_TYPE_REF));
recs[i].u.ref.refname = xstrfmt("%02"PRIuMAX, (uintmax_t)i);
}
@@ -49,7 +51,9 @@ static void t_pq_record(void)
while (!merged_iter_pqueue_is_empty(pq)) {
struct pq_entry top = merged_iter_pqueue_top(pq);
- struct pq_entry e = merged_iter_pqueue_remove(&pq);
+ struct pq_entry e;
+
+ check(!merged_iter_pqueue_remove(&pq, &e));
merged_iter_pqueue_check(&pq);
check(pq_entry_equal(&top, &e));
@@ -72,7 +76,7 @@ static void t_pq_index(void)
size_t N = ARRAY_SIZE(recs), i;
for (i = 0; i < N; i++) {
- reftable_record_init(&recs[i], BLOCK_TYPE_REF);
+ check(!reftable_record_init(&recs[i], BLOCK_TYPE_REF));
recs[i].u.ref.refname = (char *) "refs/heads/master";
}
@@ -90,7 +94,9 @@ static void t_pq_index(void)
for (i = N - 1; i > 0; i--) {
struct pq_entry top = merged_iter_pqueue_top(pq);
- struct pq_entry e = merged_iter_pqueue_remove(&pq);
+ struct pq_entry e;
+
+ check(!merged_iter_pqueue_remove(&pq, &e));
merged_iter_pqueue_check(&pq);
check(pq_entry_equal(&top, &e));
@@ -111,7 +117,7 @@ static void t_merged_iter_pqueue_top(void)
size_t N = ARRAY_SIZE(recs), i;
for (i = 0; i < N; i++) {
- reftable_record_init(&recs[i], BLOCK_TYPE_REF);
+ check(!reftable_record_init(&recs[i], BLOCK_TYPE_REF));
recs[i].u.ref.refname = (char *) "refs/heads/master";
}
@@ -129,7 +135,9 @@ static void t_merged_iter_pqueue_top(void)
for (i = N - 1; i > 0; i--) {
struct pq_entry top = merged_iter_pqueue_top(pq);
- struct pq_entry e = merged_iter_pqueue_remove(&pq);
+ struct pq_entry e;
+
+ check(!merged_iter_pqueue_remove(&pq, &e));
merged_iter_pqueue_check(&pq);
check(pq_entry_equal(&top, &e));
diff --git a/t/unit-tests/t-reftable-record.c b/t/unit-tests/t-reftable-record.c
index d49d2a2729..5954966373 100644
--- a/t/unit-tests/t-reftable-record.c
+++ b/t/unit-tests/t-reftable-record.c
@@ -17,7 +17,7 @@ static void t_copy(struct reftable_record *rec)
uint8_t typ;
typ = reftable_record_type(rec);
- reftable_record_init(&copy, typ);
+ check(!reftable_record_init(&copy, typ));
reftable_record_copy_from(&copy, rec, REFTABLE_HASH_SIZE_SHA1);
/* do it twice to catch memory leaks */
reftable_record_copy_from(&copy, rec, REFTABLE_HASH_SIZE_SHA1);
@@ -100,16 +100,20 @@ static void t_reftable_ref_record_comparison(void)
.u.ref.value.symref = (char *) "refs/heads/master",
},
};
+ int cmp;
check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check(!cmp);
check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1));
- check_int(reftable_record_cmp(&in[1], &in[2]), >, 0);
+ check(!reftable_record_cmp(&in[1], &in[2], &cmp));
+ check_int(cmp, >, 0);
in[1].u.ref.value_type = in[0].u.ref.value_type;
check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check(!cmp);
}
static void t_reftable_ref_record_compare_name(void)
@@ -209,17 +213,20 @@ static void t_reftable_log_record_comparison(void)
.u.log.update_index = 22,
},
};
+ int cmp;
check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1));
- check_int(reftable_record_cmp(&in[1], &in[2]), >, 0);
+ check(!reftable_record_cmp(&in[1], &in[2], &cmp));
+ check_int(cmp, >, 0);
/* comparison should be reversed for equal keys, because
* comparison is now performed on the basis of update indices */
- check_int(reftable_record_cmp(&in[0], &in[1]), <, 0);
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check_int(cmp, <, 0);
in[1].u.log.update_index = in[0].u.log.update_index;
check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
}
static void t_reftable_log_record_compare_key(void)
@@ -396,16 +403,20 @@ static void t_reftable_obj_record_comparison(void)
.u.obj.hash_prefix_len = 5,
},
};
+ int cmp;
check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check(!cmp);
check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1));
- check_int(reftable_record_cmp(&in[1], &in[2]), >, 0);
+ check(!reftable_record_cmp(&in[1], &in[2], &cmp));
+ check_int(cmp, >, 0);
in[1].u.obj.offset_len = in[0].u.obj.offset_len;
check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check(!cmp);
}
static void t_reftable_obj_record_roundtrip(void)
@@ -486,19 +497,24 @@ static void t_reftable_index_record_comparison(void)
.u.idx.last_key = REFTABLE_BUF_INIT,
},
};
+ int cmp;
+
check(!reftable_buf_addstr(&in[0].u.idx.last_key, "refs/heads/master"));
check(!reftable_buf_addstr(&in[1].u.idx.last_key, "refs/heads/master"));
check(!reftable_buf_addstr(&in[2].u.idx.last_key, "refs/heads/branch"));
check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check(!cmp);
check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1));
- check_int(reftable_record_cmp(&in[1], &in[2]), >, 0);
+ check(!reftable_record_cmp(&in[1], &in[2], &cmp));
+ check_int(cmp, >, 0);
in[1].u.idx.offset = in[0].u.idx.offset;
check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1));
- check(!reftable_record_cmp(&in[0], &in[1]));
+ check(!reftable_record_cmp(&in[0], &in[1], &cmp));
+ check(!cmp);
for (size_t i = 0; i < ARRAY_SIZE(in); i++)
reftable_record_release(&in[i]);
diff --git a/t/unit-tests/u-oid-array.c b/t/unit-tests/u-oid-array.c
new file mode 100644
index 0000000000..e48a433f21
--- /dev/null
+++ b/t/unit-tests/u-oid-array.c
@@ -0,0 +1,129 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
+#include "unit-test.h"
+#include "lib-oid.h"
+#include "oid-array.h"
+#include "hex.h"
+
+static void fill_array(struct oid_array *array, const char *hexes[], size_t n)
+{
+ for (size_t i = 0; i < n; i++) {
+ struct object_id oid;
+
+ cl_parse_any_oid(hexes[i], &oid);
+ oid_array_append(array, &oid);
+ }
+ cl_assert_equal_i(array->nr, n);
+}
+
+static int add_to_oid_array(const struct object_id *oid, void *data)
+{
+ struct oid_array *array = data;
+
+ oid_array_append(array, oid);
+ return 0;
+}
+
+static void t_enumeration(const char **input_args, size_t input_sz,
+ const char **expect_args, size_t expect_sz)
+{
+ struct oid_array input = OID_ARRAY_INIT, expect = OID_ARRAY_INIT,
+ actual = OID_ARRAY_INIT;
+ size_t i;
+
+ fill_array(&input, input_args, input_sz);
+ fill_array(&expect, expect_args, expect_sz);
+
+ oid_array_for_each_unique(&input, add_to_oid_array, &actual);
+ cl_assert_equal_i(actual.nr, expect.nr);
+
+ for (i = 0; i < actual.nr; i++)
+ cl_assert(oideq(&actual.oid[i], &expect.oid[i]));
+
+ oid_array_clear(&actual);
+ oid_array_clear(&input);
+ oid_array_clear(&expect);
+}
+
+#define TEST_ENUMERATION(input, expect) \
+ t_enumeration(input, ARRAY_SIZE(input), expect, ARRAY_SIZE(expect));
+
+static void t_lookup(const char **input_hexes, size_t n, const char *query_hex,
+ int lower_bound, int upper_bound)
+{
+ struct oid_array array = OID_ARRAY_INIT;
+ struct object_id oid_query;
+ int ret;
+
+ cl_parse_any_oid(query_hex, &oid_query);
+ fill_array(&array, input_hexes, n);
+ ret = oid_array_lookup(&array, &oid_query);
+
+ cl_assert(ret <= upper_bound);
+ cl_assert(ret >= lower_bound);
+
+ oid_array_clear(&array);
+}
+
+#define TEST_LOOKUP(input_hexes, query, lower_bound, upper_bound) \
+ t_lookup(input_hexes, ARRAY_SIZE(input_hexes), query, \
+ lower_bound, upper_bound);
+
+void test_oid_array__initialize(void)
+{
+ /* The hash algo is used by oid_array_lookup() internally */
+ int algo = cl_setup_hash_algo();
+ repo_set_hash_algo(the_repository, algo);
+}
+
+static const char *arr_input[] = { "88", "44", "aa", "55" };
+static const char *arr_input_dup[] = { "88", "44", "aa", "55",
+ "88", "44", "aa", "55",
+ "88", "44", "aa", "55" };
+static const char *res_sorted[] = { "44", "55", "88", "aa" };
+
+void test_oid_array__enumerate_unique(void)
+{
+ TEST_ENUMERATION(arr_input, res_sorted);
+}
+
+void test_oid_array__enumerate_duplicate(void)
+{
+ TEST_ENUMERATION(arr_input_dup, res_sorted);
+}
+
+void test_oid_array__lookup(void)
+{
+ TEST_LOOKUP(arr_input, "55", 1, 1);
+}
+
+void test_oid_array__lookup_non_existent(void)
+{
+ TEST_LOOKUP(arr_input, "33", INT_MIN, -1);
+}
+
+void test_oid_array__lookup_duplicates(void)
+{
+ TEST_LOOKUP(arr_input_dup, "55", 3, 5);
+}
+
+void test_oid_array__lookup_non_existent_dup(void)
+{
+ TEST_LOOKUP(arr_input_dup, "66", INT_MIN, -1);
+}
+
+void test_oid_array__lookup_almost_dup(void)
+{
+ const char *nearly_55;
+
+ nearly_55 = cl_setup_hash_algo() == GIT_HASH_SHA1 ?
+ "5500000000000000000000000000000000000001" :
+ "5500000000000000000000000000000000000000000000000000000000000001";
+
+ TEST_LOOKUP(((const char *[]){ "55", nearly_55 }), "55", 0, 0);
+}
+
+void test_oid_array__lookup_single_dup(void)
+{
+ TEST_LOOKUP(((const char *[]){ "55", "55" }), "55", 0, 1);
+}
diff --git a/t/unit-tests/u-oidmap.c b/t/unit-tests/u-oidmap.c
new file mode 100644
index 0000000000..dc805b7e3c
--- /dev/null
+++ b/t/unit-tests/u-oidmap.c
@@ -0,0 +1,136 @@
+#include "unit-test.h"
+#include "lib-oid.h"
+#include "oidmap.h"
+#include "hash.h"
+#include "hex.h"
+
+/*
+ * Elements we will put in oidmap structs are made of a key: the entry.oid
+ * field, which is of type struct object_id, and a value: the name field (could
+ * be a refname for example).
+ */
+struct test_entry {
+ struct oidmap_entry entry;
+ char name[FLEX_ARRAY];
+};
+
+static const char *const key_val[][2] = { { "11", "one" },
+ { "22", "two" },
+ { "33", "three" } };
+
+static struct oidmap map;
+
+void test_oidmap__initialize(void)
+{
+ oidmap_init(&map, 0);
+
+ for (size_t i = 0; i < ARRAY_SIZE(key_val); i++){
+ struct test_entry *entry;
+
+ FLEX_ALLOC_STR(entry, name, key_val[i][1]);
+ cl_parse_any_oid(key_val[i][0], &entry->entry.oid);
+ cl_assert(oidmap_put(&map, entry) == NULL);
+ }
+}
+
+void test_oidmap__cleanup(void)
+{
+ oidmap_free(&map, 1);
+}
+
+void test_oidmap__replace(void)
+{
+ struct test_entry *entry, *prev;
+
+ FLEX_ALLOC_STR(entry, name, "un");
+ cl_parse_any_oid("11", &entry->entry.oid);
+ prev = oidmap_put(&map, entry);
+ cl_assert(prev != NULL);
+ cl_assert_equal_s(prev->name, "one");
+ free(prev);
+
+ FLEX_ALLOC_STR(entry, name, "deux");
+ cl_parse_any_oid("22", &entry->entry.oid);
+ prev = oidmap_put(&map, entry);
+ cl_assert(prev != NULL);
+ cl_assert_equal_s(prev->name, "two");
+ free(prev);
+}
+
+void test_oidmap__get(void)
+{
+ struct test_entry *entry;
+ struct object_id oid;
+
+ cl_parse_any_oid("22", &oid);
+ entry = oidmap_get(&map, &oid);
+ cl_assert(entry != NULL);
+ cl_assert_equal_s(entry->name, "two");
+
+ cl_parse_any_oid("44", &oid);
+ cl_assert(oidmap_get(&map, &oid) == NULL);
+
+ cl_parse_any_oid("11", &oid);
+ entry = oidmap_get(&map, &oid);
+ cl_assert(entry != NULL);
+ cl_assert_equal_s(entry->name, "one");
+}
+
+void test_oidmap__remove(void)
+{
+ struct test_entry *entry;
+ struct object_id oid;
+
+ cl_parse_any_oid("11", &oid);
+ entry = oidmap_remove(&map, &oid);
+ cl_assert(entry != NULL);
+ cl_assert_equal_s(entry->name, "one");
+ cl_assert(oidmap_get(&map, &oid) == NULL);
+ free(entry);
+
+ cl_parse_any_oid("22", &oid);
+ entry = oidmap_remove(&map, &oid);
+ cl_assert(entry != NULL);
+ cl_assert_equal_s(entry->name, "two");
+ cl_assert(oidmap_get(&map, &oid) == NULL);
+ free(entry);
+
+ cl_parse_any_oid("44", &oid);
+ cl_assert(oidmap_remove(&map, &oid) == NULL);
+}
+
+static int key_val_contains(struct test_entry *entry, char seen[])
+{
+ for (size_t i = 0; i < ARRAY_SIZE(key_val); i++) {
+ struct object_id oid;
+
+ cl_parse_any_oid(key_val[i][0], &oid);
+
+ if (oideq(&entry->entry.oid, &oid)) {
+ if (seen[i])
+ return 2;
+ seen[i] = 1;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void test_oidmap__iterate(void)
+{
+ struct oidmap_iter iter;
+ struct test_entry *entry;
+ char seen[ARRAY_SIZE(key_val)] = { 0 };
+ int count = 0;
+
+ oidmap_iter_init(&map, &iter);
+ while ((entry = oidmap_iter_next(&iter))) {
+ if (key_val_contains(entry, seen) != 0) {
+ cl_failf("Unexpected entry: name = %s, oid = %s",
+ entry->name, oid_to_hex(&entry->entry.oid));
+ }
+ count++;
+ }
+ cl_assert_equal_i(count, ARRAY_SIZE(key_val));
+ cl_assert_equal_i(hashmap_get_size(&map.map), ARRAY_SIZE(key_val));
+}
diff --git a/t/unit-tests/u-oidtree.c b/t/unit-tests/u-oidtree.c
new file mode 100644
index 0000000000..e6eede2740
--- /dev/null
+++ b/t/unit-tests/u-oidtree.c
@@ -0,0 +1,107 @@
+#include "unit-test.h"
+#include "lib-oid.h"
+#include "oidtree.h"
+#include "hash.h"
+#include "hex.h"
+#include "strvec.h"
+
+static struct oidtree ot;
+
+#define FILL_TREE(tree, ...) \
+ do { \
+ const char *hexes[] = { __VA_ARGS__ }; \
+ if (fill_tree_loc(tree, hexes, ARRAY_SIZE(hexes))) \
+ return; \
+ } while (0)
+
+static int fill_tree_loc(struct oidtree *ot, const char *hexes[], size_t n)
+{
+ for (size_t i = 0; i < n; i++) {
+ struct object_id oid;
+ cl_parse_any_oid(hexes[i], &oid);
+ oidtree_insert(ot, &oid);
+ }
+ return 0;
+}
+
+static void check_contains(struct oidtree *ot, const char *hex, int expected)
+{
+ struct object_id oid;
+
+ cl_parse_any_oid(hex, &oid);
+ cl_assert_equal_i(oidtree_contains(ot, &oid), expected);
+}
+
+struct expected_hex_iter {
+ size_t i;
+ struct strvec expected_hexes;
+ const char *query;
+};
+
+static enum cb_next check_each_cb(const struct object_id *oid, void *data)
+{
+ struct expected_hex_iter *hex_iter = data;
+ struct object_id expected;
+
+ cl_assert(hex_iter->i < hex_iter->expected_hexes.nr);
+
+ cl_parse_any_oid(hex_iter->expected_hexes.v[hex_iter->i],
+ &expected);
+ cl_assert_equal_s(oid_to_hex(oid), oid_to_hex(&expected));
+ hex_iter->i += 1;
+ return CB_CONTINUE;
+}
+
+LAST_ARG_MUST_BE_NULL
+static void check_each(struct oidtree *ot, const char *query, ...)
+{
+ struct object_id oid;
+ struct expected_hex_iter hex_iter = { .expected_hexes = STRVEC_INIT,
+ .query = query };
+ const char *arg;
+ va_list hex_args;
+
+ va_start(hex_args, query);
+ while ((arg = va_arg(hex_args, const char *)))
+ strvec_push(&hex_iter.expected_hexes, arg);
+ va_end(hex_args);
+
+ cl_parse_any_oid(query, &oid);
+ oidtree_each(ot, &oid, strlen(query), check_each_cb, &hex_iter);
+
+ if (hex_iter.i != hex_iter.expected_hexes.nr)
+ cl_failf("error: could not find some 'object_id's for query ('%s')", query);
+
+ strvec_clear(&hex_iter.expected_hexes);
+}
+
+void test_oidtree__initialize(void)
+{
+ oidtree_init(&ot);
+}
+
+void test_oidtree__cleanup(void)
+{
+ oidtree_clear(&ot);
+}
+
+void test_oidtree__contains(void)
+{
+ FILL_TREE(&ot, "444", "1", "2", "3", "4", "5", "a", "b", "c", "d", "e");
+ check_contains(&ot, "44", 0);
+ check_contains(&ot, "441", 0);
+ check_contains(&ot, "440", 0);
+ check_contains(&ot, "444", 1);
+ check_contains(&ot, "4440", 1);
+ check_contains(&ot, "4444", 0);
+}
+
+void test_oidtree__each(void)
+{
+ FILL_TREE(&ot, "f", "9", "8", "123", "321", "320", "a", "b", "c", "d", "e");
+ check_each(&ot, "12300", "123", NULL);
+ check_each(&ot, "3211", NULL); /* should not reach callback */
+ check_each(&ot, "3210", "321", NULL);
+ check_each(&ot, "32100", "321", NULL);
+ check_each(&ot, "32", "320", "321", NULL);
+}
diff --git a/t/unit-tests/unit-test.c b/t/unit-tests/unit-test.c
index fa8818842a..5af645048a 100644
--- a/t/unit-tests/unit-test.c
+++ b/t/unit-tests/unit-test.c
@@ -1,5 +1,7 @@
#include "unit-test.h"
+#include "hex.h"
#include "parse-options.h"
+#include "strbuf.h"
#include "string-list.h"
#include "strvec.h"
diff --git a/tempfile.c b/tempfile.c
index ed88cf8431..82dfa3d82f 100644
--- a/tempfile.c
+++ b/tempfile.c
@@ -42,6 +42,8 @@
* file created by its parent.
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "abspath.h"
#include "path.h"
@@ -148,7 +150,7 @@ struct tempfile *create_tempfile_mode(const char *path, int mode)
return NULL;
}
activate_tempfile(tempfile);
- if (adjust_shared_perm(tempfile->filename.buf)) {
+ if (adjust_shared_perm(the_repository, tempfile->filename.buf)) {
int save_errno = errno;
error("cannot fix permission bits on %s", tempfile->filename.buf);
delete_tempfile(&tempfile);
diff --git a/tmp-objdir.c b/tmp-objdir.c
index 0ea078a5c5..31d16a4c2c 100644
--- a/tmp-objdir.c
+++ b/tmp-objdir.c
@@ -207,10 +207,12 @@ static int read_dir_paths(struct string_list *out, const char *path)
return 0;
}
-static int migrate_paths(struct strbuf *src, struct strbuf *dst,
+static int migrate_paths(struct tmp_objdir *t,
+ struct strbuf *src, struct strbuf *dst,
enum finalize_object_file_flags flags);
-static int migrate_one(struct strbuf *src, struct strbuf *dst,
+static int migrate_one(struct tmp_objdir *t,
+ struct strbuf *src, struct strbuf *dst,
enum finalize_object_file_flags flags)
{
struct stat st;
@@ -219,11 +221,11 @@ static int migrate_one(struct strbuf *src, struct strbuf *dst,
return -1;
if (S_ISDIR(st.st_mode)) {
if (!mkdir(dst->buf, 0777)) {
- if (adjust_shared_perm(dst->buf))
+ if (adjust_shared_perm(t->repo, dst->buf))
return -1;
} else if (errno != EEXIST)
return -1;
- return migrate_paths(src, dst, flags);
+ return migrate_paths(t, src, dst, flags);
}
return finalize_object_file_flags(src->buf, dst->buf, flags);
}
@@ -233,7 +235,8 @@ static int is_loose_object_shard(const char *name)
return strlen(name) == 2 && isxdigit(name[0]) && isxdigit(name[1]);
}
-static int migrate_paths(struct strbuf *src, struct strbuf *dst,
+static int migrate_paths(struct tmp_objdir *t,
+ struct strbuf *src, struct strbuf *dst,
enum finalize_object_file_flags flags)
{
size_t src_len = src->len, dst_len = dst->len;
@@ -255,7 +258,7 @@ static int migrate_paths(struct strbuf *src, struct strbuf *dst,
if (is_loose_object_shard(name))
flags_copy |= FOF_SKIP_COLLISION_CHECK;
- ret |= migrate_one(src, dst, flags_copy);
+ ret |= migrate_one(t, src, dst, flags_copy);
strbuf_setlen(src, src_len);
strbuf_setlen(dst, dst_len);
@@ -283,7 +286,7 @@ int tmp_objdir_migrate(struct tmp_objdir *t)
strbuf_addbuf(&src, &t->path);
strbuf_addstr(&dst, repo_get_object_directory(t->repo));
- ret = migrate_paths(&src, &dst, 0);
+ ret = migrate_paths(t, &src, &dst, 0);
strbuf_release(&src);
strbuf_release(&dst);
diff --git a/trace2.h b/trace2.h
index 901f39253a..e4f23784e4 100644
--- a/trace2.h
+++ b/trace2.h
@@ -31,7 +31,7 @@
*
* For more info about: trace2 targets, conventions for public functions and
* macros, trace2 target formats and examples on trace2 API usage refer to
- * Documentation/technical/api-trace2.txt
+ * Documentation/technical/api-trace2.adoc
*
*/
diff --git a/trace2/tr2_sysenv.c b/trace2/tr2_sysenv.c
index 01379c5cad..4abc218514 100644
--- a/trace2/tr2_sysenv.c
+++ b/trace2/tr2_sysenv.c
@@ -7,7 +7,7 @@
/*
* Each entry represents a trace2 setting.
- * See Documentation/technical/api-trace2.txt
+ * See Documentation/technical/api-trace2.adoc
*/
struct tr2_sysenv_entry {
const char *env_var_name;
diff --git a/transport-helper.c b/transport-helper.c
index d457b42550..69391ee7d2 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -162,7 +162,7 @@ static struct child_process *get_helper(struct transport *transport)
data->helper = helper;
data->no_disconnect_req = 0;
- refspec_init(&data->rs, REFSPEC_FETCH);
+ refspec_init_fetch(&data->rs);
/*
* Open the output as FILE* so strbuf_getline_*() family of
diff --git a/transport.h b/transport.h
index 44100fa9b7..892f19454a 100644
--- a/transport.h
+++ b/transport.h
@@ -168,7 +168,7 @@ struct transport *transport_get(struct remote *, const char *);
* Check whether a transport is allowed by the environment.
*
* Type should generally be the URL scheme, as described in
- * Documentation/git.txt
+ * Documentation/git.adoc
*
* from_user specifies if the transport was given by the user. If unknown pass
* a -1 to read from the environment to determine if the transport was given by
diff --git a/unpack-trees.c b/unpack-trees.c
index 334cb84f65..cf5b73c84b 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -2904,7 +2904,7 @@ int threeway_merge(const struct cache_entry * const *stages,
* The rule is to "carry forward" what is in the index without losing
* information across a "fast-forward", favoring a successful merge
* over a merge failure when it makes sense. For details of the
- * "carry forward" rule, please see <Documentation/git-read-tree.txt>.
+ * "carry forward" rule, please see <Documentation/git-read-tree.adoc>.
*
*/
int twoway_merge(const struct cache_entry * const *src,
diff --git a/upload-pack.c b/upload-pack.c
index 728b2477fc..7498b45e2e 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -32,6 +32,7 @@
#include "write-or-die.h"
#include "json-writer.h"
#include "strmap.h"
+#include "promisor-remote.h"
/* Remember to update object flag allocation in object.h */
#define THEY_HAVE (1u << 11)
@@ -319,6 +320,8 @@ static void create_pack_file(struct upload_pack_data *pack_data,
strvec_push(&pack_objects.args, "--delta-base-offset");
if (pack_data->use_include_tag)
strvec_push(&pack_objects.args, "--include-tag");
+ if (repo_has_accepted_promisor_remote(the_repository))
+ strvec_push(&pack_objects.args, "--missing=allow-promisor");
if (pack_data->filter_options.choice) {
const char *spec =
expand_list_objects_filter_spec(&pack_data->filter_options);
diff --git a/worktree.c b/worktree.c
index d4a68c9c23..c34b9eb74e 100644
--- a/worktree.c
+++ b/worktree.c
@@ -59,8 +59,9 @@ static void add_head_info(struct worktree *wt)
static int is_current_worktree(struct worktree *wt)
{
char *git_dir = absolute_pathdup(repo_get_git_dir(the_repository));
- const char *wt_git_dir = get_worktree_git_dir(wt);
+ char *wt_git_dir = get_worktree_git_dir(wt);
int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir));
+ free(wt_git_dir);
free(git_dir);
return is_current;
}
@@ -127,7 +128,7 @@ struct worktree *get_linked_worktree(const char *id,
if (!id)
die("Missing linked worktree name");
- strbuf_git_common_path(&path, the_repository, "worktrees/%s/gitdir", id);
+ repo_common_path_append(the_repository, &path, "worktrees/%s/gitdir", id);
if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
/* invalid gitdir file */
goto done;
@@ -198,14 +199,19 @@ struct worktree **get_worktrees(void)
return get_worktrees_internal(0);
}
-const char *get_worktree_git_dir(const struct worktree *wt)
+struct worktree **get_worktrees_without_reading_head(void)
+{
+ return get_worktrees_internal(1);
+}
+
+char *get_worktree_git_dir(const struct worktree *wt)
{
if (!wt)
- return repo_get_git_dir(the_repository);
+ return xstrdup(repo_get_git_dir(the_repository));
else if (!wt->id)
- return repo_get_common_dir(the_repository);
+ return xstrdup(repo_get_common_dir(the_repository));
else
- return git_common_path("worktrees/%s", wt->id);
+ return repo_common_path(the_repository, "worktrees/%s", wt->id);
}
static struct worktree *find_worktree_by_suffix(struct worktree **list,
@@ -336,6 +342,7 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
{
struct strbuf wt_path = STRBUF_INIT;
struct strbuf realpath = STRBUF_INIT;
+ struct strbuf buf = STRBUF_INIT;
char *path = NULL;
int err, ret = -1;
@@ -365,7 +372,7 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
if (!is_absolute_path(wt->path)) {
strbuf_addf_gently(errmsg,
_("'%s' file does not contain absolute path to the working tree location"),
- git_common_path("worktrees/%s/gitdir", wt->id));
+ repo_common_path_replace(the_repository, &buf, "worktrees/%s/gitdir", wt->id));
goto done;
}
@@ -387,14 +394,16 @@ int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
goto done;
}
- strbuf_realpath(&realpath, git_common_path("worktrees/%s", wt->id), 1);
+ strbuf_realpath(&realpath, repo_common_path_replace(the_repository, &buf, "worktrees/%s", wt->id), 1);
ret = fspathcmp(path, realpath.buf);
if (ret)
strbuf_addf_gently(errmsg, _("'%s' does not point back to '%s'"),
- wt->path, git_common_path("worktrees/%s", wt->id));
+ wt->path, repo_common_path_replace(the_repository, &buf,
+ "worktrees/%s", wt->id));
done:
free(path);
+ strbuf_release(&buf);
strbuf_release(&wt_path);
strbuf_release(&realpath);
return ret;
@@ -406,11 +415,13 @@ void update_worktree_location(struct worktree *wt, const char *path_,
struct strbuf path = STRBUF_INIT;
struct strbuf dotgit = STRBUF_INIT;
struct strbuf gitdir = STRBUF_INIT;
+ char *wt_gitdir;
if (is_main_worktree(wt))
BUG("can't relocate main worktree");
- strbuf_realpath(&gitdir, git_common_path("worktrees/%s/gitdir", wt->id), 1);
+ wt_gitdir = repo_common_path(the_repository, "worktrees/%s/gitdir", wt->id);
+ strbuf_realpath(&gitdir, wt_gitdir, 1);
strbuf_realpath(&path, path_, 1);
strbuf_addf(&dotgit, "%s/.git", path.buf);
if (fspathcmp(wt->path, path.buf)) {
@@ -422,6 +433,7 @@ void update_worktree_location(struct worktree *wt, const char *path_,
strbuf_release(&path);
strbuf_release(&dotgit);
strbuf_release(&gitdir);
+ free(wt_gitdir);
}
int is_worktree_being_rebased(const struct worktree *wt,
@@ -510,7 +522,8 @@ int submodule_uses_worktrees(const char *path)
int ret = 0;
struct repository_format format = REPOSITORY_FORMAT_INIT;
- submodule_gitdir = git_pathdup_submodule(path, "%s", "");
+ submodule_gitdir = repo_submodule_path(the_repository,
+ path, "%s", "");
if (!submodule_gitdir)
return 0;
@@ -606,6 +619,7 @@ static void repair_gitfile(struct worktree *wt,
struct strbuf backlink = STRBUF_INIT;
char *dotgit_contents = NULL;
const char *repair = NULL;
+ char *path = NULL;
int err;
/* missing worktree can't be repaired */
@@ -617,7 +631,8 @@ static void repair_gitfile(struct worktree *wt,
goto done;
}
- strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1);
+ path = repo_common_path(the_repository, "worktrees/%s", wt->id);
+ strbuf_realpath(&repo, path, 1);
strbuf_addf(&dotgit, "%s/.git", wt->path);
strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
dotgit_contents = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
@@ -647,6 +662,7 @@ static void repair_gitfile(struct worktree *wt,
done:
free(dotgit_contents);
+ free(path);
strbuf_release(&repo);
strbuf_release(&dotgit);
strbuf_release(&gitdir);
@@ -678,11 +694,13 @@ void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path
struct strbuf gitdir = STRBUF_INIT;
struct strbuf dotgit = STRBUF_INIT;
int is_relative_path;
+ char *path = NULL;
if (is_main_worktree(wt))
goto done;
- strbuf_realpath(&gitdir, git_common_path("worktrees/%s/gitdir", wt->id), 1);
+ path = repo_common_path(the_repository, "worktrees/%s/gitdir", wt->id);
+ strbuf_realpath(&gitdir, path, 1);
if (strbuf_read_file(&dotgit, gitdir.buf, 0) < 0)
goto done;
@@ -701,6 +719,7 @@ void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path
done:
strbuf_release(&gitdir);
strbuf_release(&dotgit);
+ free(path);
}
void repair_worktrees_after_gitdir_move(const char *old_path)
@@ -754,8 +773,7 @@ static ssize_t infer_backlink(const char *gitfile, struct strbuf *inferred)
id++; /* advance past '/' to point at <id> */
if (!*id)
goto error;
- strbuf_reset(inferred);
- strbuf_git_common_path(inferred, the_repository, "worktrees/%s", id);
+ repo_common_path_replace(the_repository, inferred, "worktrees/%s", id);
if (!is_directory(inferred->buf))
goto error;
@@ -893,7 +911,11 @@ int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath,
ssize_t read_result;
*wtpath = NULL;
- strbuf_realpath(&repo, git_common_path("worktrees/%s", id), 1);
+
+ path = repo_common_path(the_repository, "worktrees/%s", id);
+ strbuf_realpath(&repo, path, 1);
+ FREE_AND_NULL(path);
+
strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
if (!is_directory(repo.buf)) {
strbuf_addstr(reason, _("not a valid directory"));
diff --git a/worktree.h b/worktree.h
index 38145df80f..e4bcccdc0a 100644
--- a/worktree.h
+++ b/worktree.h
@@ -31,6 +31,14 @@ struct worktree {
struct worktree **get_worktrees(void);
/*
+ * Like `get_worktrees`, but does not read HEAD. Skip reading HEAD allows to
+ * get the worktree without worrying about failures pertaining to parsing
+ * the HEAD ref. This is useful in contexts where it is assumed that the
+ * refdb may not be in a consistent state.
+ */
+struct worktree **get_worktrees_without_reading_head(void);
+
+/*
* Returns 1 if linked worktrees exist, 0 otherwise.
*/
int submodule_uses_worktrees(const char *path);
@@ -39,7 +47,7 @@ int submodule_uses_worktrees(const char *path);
* Return git dir of the worktree. Note that the path may be relative.
* If wt is NULL, git dir of current worktree is returned.
*/
-const char *get_worktree_git_dir(const struct worktree *wt);
+char *get_worktree_git_dir(const struct worktree *wt);
/*
* Search for the worktree identified unambiguously by `arg` -- typically
diff --git a/wt-status.c b/wt-status.c
index 3ee9181764..1da5732f57 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1289,7 +1289,8 @@ static void show_am_in_progress(struct wt_status *s,
static char *read_line_from_git_path(const char *filename)
{
struct strbuf buf = STRBUF_INIT;
- FILE *fp = fopen_or_warn(git_path("%s", filename), "r");
+ FILE *fp = fopen_or_warn(repo_git_path_append(the_repository, &buf,
+ "%s", filename), "r");
if (!fp) {
strbuf_release(&buf);
@@ -1383,27 +1384,33 @@ static void abbrev_oid_in_line(struct strbuf *line)
static int read_rebase_todolist(const char *fname, struct string_list *lines)
{
- struct strbuf line = STRBUF_INIT;
- FILE *f = fopen(git_path("%s", fname), "r");
+ struct strbuf buf = STRBUF_INIT;
+ FILE *f = fopen(repo_git_path_append(the_repository, &buf, "%s", fname), "r");
+ int ret;
if (!f) {
- if (errno == ENOENT)
- return -1;
+ if (errno == ENOENT) {
+ ret = -1;
+ goto out;
+ }
die_errno("Could not open file %s for reading",
- git_path("%s", fname));
+ repo_git_path_replace(the_repository, &buf, "%s", fname));
}
- while (!strbuf_getline_lf(&line, f)) {
- if (starts_with(line.buf, comment_line_str))
+ while (!strbuf_getline_lf(&buf, f)) {
+ if (starts_with(buf.buf, comment_line_str))
continue;
- strbuf_trim(&line);
- if (!line.len)
+ strbuf_trim(&buf);
+ if (!buf.len)
continue;
- abbrev_oid_in_line(&line);
- string_list_append(lines, line.buf);
+ abbrev_oid_in_line(&buf);
+ string_list_append(lines, buf.buf);
}
fclose(f);
- strbuf_release(&line);
- return 0;
+
+ ret = 0;
+out:
+ strbuf_release(&buf);
+ return ret;
}
static void show_rebase_information(struct wt_status *s,
@@ -1434,9 +1441,12 @@ static void show_rebase_information(struct wt_status *s,
i < have_done.nr;
i++)
status_printf_ln(s, color, " %s", have_done.items[i].string);
- if (have_done.nr > nr_lines_to_show && s->hints)
+ if (have_done.nr > nr_lines_to_show && s->hints) {
+ char *path = repo_git_path(the_repository, "rebase-merge/done");
status_printf_ln(s, color,
- _(" (see more in file %s)"), git_path("rebase-merge/done"));
+ _(" (see more in file %s)"), path);
+ free(path);
+ }
}
if (yet_to_do.nr == 0)
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index bb56b23f34..2cecde5afe 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -85,7 +85,7 @@ typedef struct s_xpparam {
regex_t **ignore_regex;
size_t ignore_regex_nr;
- /* See Documentation/diff-options.txt. */
+ /* See Documentation/diff-options.adoc. */
char **anchors;
size_t anchors_nr;
} xpparam_t;
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index f8e3f25b03..1d40c9cb40 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -43,6 +43,10 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *
return 0;
}
+static long saturating_add(long a, long b)
+{
+ return signed_add_overflows(a, b) ? LONG_MAX : a + b;
+}
/*
* Starting at the passed change atom, find the latest change atom to be included
@@ -52,7 +56,9 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *
xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
{
xdchange_t *xch, *xchp, *lxch;
- long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
+ long max_common = saturating_add(saturating_add(xecfg->ctxlen,
+ xecfg->ctxlen),
+ xecfg->interhunkctxlen);
long max_ignorable = xecfg->ctxlen;
long ignored = 0; /* number of ignored blank lines */
diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c
index 82f663004e..77dc411d19 100644
--- a/xdiff/xpatience.c
+++ b/xdiff/xpatience.c
@@ -64,7 +64,7 @@ struct hashmap {
/*
* If 1, this entry can serve as an anchor. See
- * Documentation/diff-options.txt for more information.
+ * Documentation/diff-options.adoc for more information.
*/
unsigned anchor : 1;
} *entries, *first, *last;